简体   繁体   中英

CameraX Saving images to file, FileNotFoundException: (Permission Denied)

I'm working with Android Studio, writing in Java. I'm trying to write a piece of software that will take pictures from a camera based on an event. So first steps first was to figure out the API. Camera is depreciated, so there's Camera2, but the latest is CameraX. I've attempted to use both CameraX and Camera2, but for some reason I'm not saving any images. I believe the capture works, but the issue is saving the files. I don't know what's going on.

First lets start with permissions. Yes, I have set permissions for both the camera and external storage.

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

  

So lets move on to the code itself.

I've never saved photos before, but I have worked with text files such as.txt and.csv files. Which I've been able to make work.

Right now I do a simple button click to capture the image. Which is nothing more than an onclicklistener. Once I press the button this is what actions occur.

                SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
                File file = new File(Environment.getDataDirectory(), mDateFormat.format(new Date())+ ".jpg");

                if(!file.exists())
                    file.mkdirs();

                ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
                imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback () {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        Toast.makeText(MainActivity.this, "TEST", Toast.LENGTH_SHORT).show();

                        new Handler().post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this, "Image Saved successfully", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                    @Override
                    public void onError(@NonNull ImageCaptureException error) {
                        error.printStackTrace();
//                        Toast.makeText(MainActivity.this, "Image save FAILED!!!!", Toast.LENGTH_SHORT).show();
                    }
                });

when the app launches the first time, it asks for permission and I grant permission. but looking at the run tab as part of the Android Studio ID, I get the following readout, when I press the capture button

D/ImageCapture: Send image capture request [current, pending] = [0, 1]
D/CaptureSession: Issuing capture request.
W/example.camera: JNI critical lock held for 21.148ms on Thread[14,tid=10076,Runnable,Thread*=0xdc743000,peer=0x13184cf8,"Binder:10045_2"]
W/ExifInterface: Stop reading file since a wrong offset may cause an infinite loop: 0
    Skip the tag entry since data format (ULONG) is unexpected for tag: GPSAltitudeRef
W/ExifInterface: Stop reading file since a wrong offset may cause an infinite loop: 0
    Stop reading file since a wrong offset may cause an infinite loop: 0
W/System.err: androidx.camera.core.ImageCaptureException: Failed to write or close the file
W/System.err:     at androidx.camera.core.ImageCapture$3.onError(ImageCapture.java:669)
        at androidx.camera.core.ImageSaver.lambda$postError$1$ImageSaver(ImageSaver.java:263)
        at androidx.camera.core.-$$Lambda$ImageSaver$eAp-cZyzsEk-LVLazzLE-ezQzwo.run(Unknown Source:8)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
    Caused by: java.io.FileNotFoundException: /data/20200728142756.jpg: open failed: EACCES (Permission denied)
        at libcore.io.IoBridge.open(IoBridge.java:496)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:235)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
        at androidx.camera.core.ImageSaver.run(ImageSaver.java:97)
        ... 3 more
W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
        at libcore.io.Linux.open(Native Method)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
        at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7255)
        at libcore.io.IoBridge.open(IoBridge.java:482)
        ... 6 more
W/example.camera: JNI critical lock held for 17.651ms on Thread[16,tid=10083,Runnable,Thread*=0xdc74bc00,peer=0x13184de8,"Binder:10045_4"]

I've searched around, and looked for different methods of saving the file, and I've had success with saving the image, sort of, when I use the camera2 api and save the file using outputstream

When I use Camera2 it appears to save just fine, but I have no idea where it saves, I can't find it. I point this out, because it still appears to be a permission denied error as what I'm seeing is when I open up Device File Explorer its not letting me view /storage/emulator/ it just shows me

ls: /storage/emulated/: Permission denied. 

在此处输入图像描述

all the examples I have found have assumed access with no issues to emulated.

Does anyone have any insight as to why I'm having permission denied errors?

UPDATE

So I still haven't solved the issue related to the failed mkdir() call. However, I did fix one issue, that could have been causing the issue. The means as to how I was creating the directory and the file itself wasn't correct.

SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
File file = new File(getBatchDirectoryName(), mDateFormat.format(new Date())+ ".jpg");

public String getBatchDirectoryName() {

        String app_folder_path = "";
        app_folder_path = Environment.getExternalStorageDirectory() + "/images";
        File dir = new File(app_folder_path);
        if (!dir.exists() && !dir.mkdirs()) {
            Toast.makeText(MainActivity.this, "Trip", Toast.LENGTH_SHORT).show();
        }

        return app_folder_path;
    }

However, it still fails to create the directory. Which still makes me come back to a permission issue.

Environment.getExternalStorageDirectory() appears to be deprecated. Documentation appears to recommend using getExternalFilesDir(string) or MediaStore. However, I'm having issues finding sample use cases. And I can't imagine, that while deprecated, it would just stop working at all. Most devices on the market are not API 29 or higher.

Based on help from @blackapps, I was able to make changes to my Manifest file to allow access to external storage.

More information can be found here

Request Legacy External Storage

With @blackapps, and this thread on stackoverflow I was able to update my permissions to allow legacy access.

Because in Android Q they have disabled direct file access, they have added a work around to allow legacy support. This is, and as others have mentioned, only a temporary fix for the issue. You will need to follow the new format for saving files going forward with Android R. In my situation, I am only developing a proof of concept and will not need to worry about future versions of Android.

To make the temporary fix, do the following

Add the following line

android:requestLegacyExternalStorage="true">

inside your <application bracket on the manifest file. Should look similar to this

    <?xml version="1.0" encoding="utf-8"?>
...
    package="com.example.camerax">

...

    <application
        ...
        android:requestLegacyExternalStorage="true">
        <activity android:name=".MainActivity">
            <intent-filter>
                ...
            </intent-filter>
        </activity>
    </application>

remember this is not a long term fix, and will not work when a device is updated to Android R

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM