相机意图未保存照片

21

我之前已经成功使用过这段代码,但是文件路径指向的位置在SD卡上。

final File temp = new File(getCacheDir(), "temp.jpg");
temp.delete();
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(temp));
startActivityForResult(intent, CONFIG.Intents.Actions.SELECT_CAMERA_PHOTO);

但是当我使用getCacheDir而不是SD卡上的位置时,似乎照片从未被保存。这是缓存目录和图像捕获的限制吗?

2个回答

42

从技术上讲,这是因为使用相机应用程序捕获图像时不支持写入内部存储。实际上,您可能会在logcat中注意到打印了一个异常,指出“不支持写入内部存储”。然而,这不起作用的真正原因是,默认情况下,您正在创建一个对您的应用程序包私有的文件,另一个应用程序(即相机应用程序)无法访问该文件位置,因为它没有权限这样做。外部存储是文件系统中唯一可以全局访问的部分。

解决方法是为您创建具有全局(WORLD_WRITEABLE)权限的文件。通常,这使得相机应用程序可以通过传递的Uri访问文件。实际上,在File上并没有直接执行此操作的方法,因此您必须使用Context中可用的方法创建文件,然后在此之后获取一个文件句柄:

//Remove if exists, the file MUST be created using the lines below
File f = new File(getFilesDir(), "Captured.jpg");
f.delete();
//Create new file
FileOutputStream fos = openFileOutput("Captured.jpg", Context.MODE_WORLD_WRITEABLE);
fos.close();
//Get reference to the file
File f = new File(getFilesDir(), "Captured.jpg");

这也有点限制了你可以放置文件的位置,因为Context方法本质上是在根目录下的"files"目录中创建文件,而你不能将其重定向到缓存目录。

希望对你有所帮助。


太好了,我就有预感是这个问题。我想我会再次将保存照片暂时移动到SD卡上,但我会把这个加入我的工具箱 :) - sgarman
1
有关如何解决Context.MODE_WORLD_WRITEABLE弃用的任何想法吗? - bre_dev
6
这里是来自这里的内容:MODE_WORLD_WRITEABLE 在API等级17中已被弃用。创建可供全局写入的文件非常危险,很可能导致应用程序存在安全漏洞。强烈不建议使用; - Atul
1
过去5年里,事情确实发生了很大的变化 :) ContentProvider(例如FileProvider)是解决这个问题的最佳方案。 - devunwired

30

我找到的最佳解决方案是:使用FileProvider(需要support-library-v4)
它使用内部存储! https://developer.android.com/reference/android/support/v4/content/FileProvider.html

  1. 在Manifest文件中的Application标签中定义你的FileProvider:

<provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="your.package.name.fileprovider"
      android:exported="false"
      android:grantUriPermissions="true" >
      <meta-data
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/image_path" />
</provider>
  • 如果必要的话,请将相机功能添加到AndroidManifest.xml的根元素中:

  • <uses-feature android:name="android.hardware.camera"
    android:required="true" />
    
  • 在例如res/xml/image_path.xml中定义您的图像路径:

  • <paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="captured_image" path="your/path/"/>
    </paths>
    
  • Java:

    private static final int IMAGE_REQUEST_CODE = 1;
    // your authority, must be the same as in your manifest file 
    private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
    
  • 4.1 捕获意图:

        File path = new File(activity.getFilesDir(), "your/path");
        if (!path.exists()) path.mkdirs();
        File image = new File(path, "image.jpg");
        Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, IMAGE_REQUEST_CODE);
    

    4.2 onActivityResult():

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent intent) {
            if (requestCode == IMAGE_REQUEST_CODE) {
                if (resultCode == Activity.RESULT_OK) {
                    File path = new File(getFilesDir(), "your/path");
                    if (!path.exists()) path.mkdirs();
                    File imageFile = new File(path, "image.jpg");
                    // use imageFile to open your image
                }
            }
            super.onActivityResult(requestCode, resultCode, intent);
        }
    

    你确定需要在FileProvider中使用READ_EXTERNAL_STORAGE权限吗?我认为这是不正确的。 - Martin Konecny
    马丁,我认为你是对的。我会改变我的答案。 - Harry
    @Harry 如果我想捕获多张图片怎么办?我们必须在xml的captured_image中硬编码图像名称吗? - Smeet
    image_path.xml 中的 <files-path> 是什么作用?我没有看到代码中引用 captured_image 路径的地方。 - Chuck Krutsinger
    这是照片的文件名,如果我没记错的话。XML文件需要在AndroidManifest.xml中定义(请参见步骤1)。 - Harry
    显示剩余3条评论

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接