【问题标题】:CameraX Saving images to file, FileNotFoundException: (Permission Denied)CameraX 将图像保存到文件,FileNotFoundException: (Permission Denied)
【发布时间】:2020-07-28 18:57:56
【问题描述】:

我正在使用 Android Studio,用 Java 编写代码。我正在尝试编写一个软件,该软件将根据事件从相机中拍照。所以第一步首先是弄清楚API。相机折旧,所以有Camera2,但最新的是CameraX。我尝试同时使用 CameraX 和 Camera2,但由于某种原因,我没有保存任何图像。我相信捕获有效,但问题是保存文件。不知道怎么回事。

首先让我们从权限开始。是的,我已经为相机和外部存储设置了权限。

    <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"/>

  

让我们继续讨论代码本身。

我以前从未保存过照片,但我使用过文本文件,例如 .txt 和 .csv 文件。我已经成功了。

现在我单击一个简单的按钮来捕获图像。这只不过是一个 onclicklistener。一旦我按下按钮,这就是发生的动作。

                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();
                    }
                });

当应用程序第一次启动时,它会请求许可,我会授予许可。但是将运行选项卡视为 Android Studio ID 的一部分,当我按下捕获按钮时,我会得到以下读数

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"]

我四处搜索,寻找保存文件的不同方法,当我使用 camera2 api 并使用 outputstream 保存文件时,我已经成功保存了图像

当我使用 Camera2 时,它似乎保存得很好,但我不知道它保存在哪里,我找不到它。我指出这一点,因为它似乎仍然是一个权限被拒绝错误,因为我看到的是当我打开设备文件资源管理器时它不允许我查看 /storage/emulator/ 它只是显示给我

ls: /storage/emulated/: Permission denied. 

我发现的所有示例都假定访问没有问题。

有人知道为什么我会出现权限被拒绝错误吗?

更新

所以我仍然没有解决与失败的 mkdir() 调用相关的问题。但是,我确实解决了一个问题,这可能是导致问题的原因。关于我如何创建目录和文件本身的方法不正确。

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;
    }

但是,它仍然无法创建目录。这仍然让我回到权限问题。

Environment.getExternalStorageDirectory() 似乎已被弃用。文档似乎建议使用 getExternalFilesDir(string) 或 MediaStore。但是,我在查找示例用例时遇到了问题。而且我无法想象,虽然已被弃用,但它会完全停止工作。市场上的大多数设备都不是 API 29 或更高版本。

【问题讨论】:

  • 您的 File 文件定义了一个 jpg 文件,但随后您使用 file.mkdirs();在它上面创建一个目录。
  • file.mkdirs(); 检查返回值,因为它可能是 fsil。如果是这样,则显示敬酒以告知用户并返回。不要继续!
  • @blackapps 你好。所以 file.mkdirs() 的返回总是假的。仔细查看文档,它特别指出“如果创建目录则返回 true,否则返回 false”。所以看看我是如何在 mkdirs() 之前检查它是否存在的,我假设它意味着 True 是成功创建的,而 false 意味着创建失败。
  • 请在此处调整您的代码。并决定是否应该成为一个目录。我觉得它不对。
  • @blackapps 我修复了与创建带有 jpeg 文件名称的导演相关的问题,但似乎仍然存在某种性质的权限错误。我更新了我的帖子以反映我所做的和发现的。

标签: java android


【解决方案1】:

在@blackapps 的帮助下,我能够更改我的清单文件以允许访问外部存储。

更多信息可以在这里找到

Request Legacy External Storage

使用 @blackapps 和 stackoverflow 上的这个线程,我能够更新我的权限以允许旧版访问。

因为在 Android Q 中他们禁用了直接文件访问,所以他们添加了一种变通方法以允许旧版支持。正如其他人所提到的,这只是该问题的临时解决方案。在 Android R 中,您需要遵循新的文件保存格式。在我的情况下,我只是在开发概念验证,不需要担心未来的 Android 版本。

要进行临时修复,请执行以下操作

添加以下行

android:requestLegacyExternalStorage="true">

在清单文件的

    <?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>

请记住,这不是长期修复,并且在设备更新到 Android R 后将无法正常工作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 2013-05-31
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2021-07-11
    相关资源
    最近更新 更多