前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )

FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )

作者头像
全栈程序员站长
发布2022-11-16 14:20:20
1.3K0
发布2022-11-16 14:20:20
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

Add on 2020-9-24: 可以参考源码 CameraDemo 的file_provider分支上的代码。

以前调用系统相机拍照的时候,流程是这样的

代码语言:javascript
复制
 private void takePhoto() { 

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 

//创建一个路径保存图片
photoFile = ImageUtil.createImageFile();
if (photoFile != null) { 

photoURI = Uri.fromFile(photoFile);
//传递一个Uri
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);
}
}
}

然后在onActivityResult方法中处理拍照结果。

代码语言:javascript
复制
  @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 

super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) { 

case TAKE_PHOTO:
if (resultCode == RESULT_OK) { 

//处理拍照的结果
processTakePhoto(photoFile.getPath());
}
break;
default:
break;
}
}

但是发现在7.0的系统上直接崩溃了,错误如下。

这里写图片描述
这里写图片描述
代码语言:javascript
复制
android.os.FileUriExposedException: 
file:///storage/emulated/0/Android/data/com.hm.camerademo/files/Picture
s/20170225_140305187933259.jpg exposed beyond app through 
ClipData.Item.getUri()

然后网上搜了一把,是 photoURI = Uri.fromFile(photoFile); 这种创建Uri的方式有问题了,不够安全。需要使用FileProvider来创建Uri.

使用FileProvider四部曲

第一步,指定一个FileProvider。在AndroidManifest.xml中声明一个条目

代码语言:javascript
复制
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp">
<application ...>
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>

com.example.myapp是你的包名

第二步,指定想分享的目录。在res目录下新建一个xml目录,在xml目录下面新建一个xml文件。我新建的文件名叫filepaths.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录-->
<!--/storage/emulated/0/DCIM/camerademo-->
<external-path name="hm_DCIM" path="DCIM/camerademo" />
<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录-->
<!--/storage/emulated/0/Pictures/camerademo-->
<external-path name="hm_Pictures" path="Pictures/camerademo" />
<!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录-->
<!--/data/user/0/com.hm.camerademo/files/images-->
<files-path name="hm_private_files" path="images" />
<!-- 代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 -->
<!--/data/user/0/com.hm.camerademo/cache/images-->
<cache-path name="hm_private_cache" path="images" />
<!--代表app外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录-->
<!--/storage/emulated/0/Android/data/com.hm.camerademo/files/Pictures-->
<external-files-path name="hm_external_files" path="Pictures" />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录-->
<!--/storage/emulated/0/Android/data/com.hm.camerademo/cache/images-->
<external-cache-path name="hm_external_cache" path="images" />
</paths>

name=“name” URI 路径段,取值会隐藏你分享的目录的名字。比如下面这个

代码语言:javascript
复制
  <!--/storage/emulated/0/Android/data/com.hm.camerademo/cache/images-->
<external-cache-path name="hm_file" path="images" />

会用hm_file 替代/storage/emulated/0/Android/data/com.hm.camerademo/cache/images

path=“path” 你分享的目录的名字

注意

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg错误产生原因。

代码语言:javascript
复制
  <external-path name="hm_DCIM" path="DCIM/camerademo" />
<external-path name="hm_Pictures" path="Pictures/camerademo" />

我可以在 external-path目录下指定多个我想分享的目录,两个分享的目录的name取值不应该相同。我把上面两个的name字段都叫 hm_file,然后看看有什么问题。结果就是会报标题上的那个错误,实验一把

代码语言:javascript
复制
 <external-path name="hm_file" path="DCIM/camerademo" />
<external-path name="hm_file" path="Pictures/camerademo" />

然后我生成一个Content URI。

代码语言:javascript
复制
  File imageFile = null;
String storagePath;
File storageDir;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
try { 

//文件路径是公共的DCIM目录下的/camerademo目录
storagePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ 
File.separator + "DCIM" + File.separator + "camerademo";
storageDir = new File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
Log.e(TAG, imageFile.getAbsolutePath());
} catch (IOException e) { 

e.printStackTrace();
}
return imageFile;
代码语言:javascript
复制
//文件路径
/storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg
代码语言:javascript
复制
//生成Uri
photoFile = ImageUtil.createImageFile();
photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);

但是报错了。错误如下

代码语言:javascript
复制
 java.lang.IllegalArgumentException: Failed to find configured root 
that contains 
/storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg

我把上面生成文件的路径改一下

代码语言:javascript
复制
//路径是公共存储路径Pictures目录下的camerademo目录
storagePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ 
File.separator + "Pictures" + File.separator + "camerademo";
代码语言:javascript
复制
//生成的文件路径
/storage/emulated/0/Pictures/camerademo/20170226_1104551680202685.jpg
//可以正常生成Uri的路径
/hm_file/20170226_1104551680202685.jpg

上面的问题说明 在filePath.xml 文件中,如果要在同一个存储路径下,指定两个共享的目录,如下所示,那么两个共享路径的name字段取值不应该相同,如果两者相同,那么后面的一行指定的path(/storage/emulated/0/Pictures/camerademo)会覆盖上面一行指定的path(/storage/emulated/0/DCIM/camerademo)

代码语言:javascript
复制
//	共享目录的根目录都是 /storage/emulated/0/
<external-path name="hm_file" path="DCIM/camerademo" />
<external-path name="hm_file" path="Pictures/camerademo" />

第三步 为一个文件生成 Content URI

代码语言:javascript
复制
   File imageFile = null;
String storagePath;
File storageDir;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
try { 

storagePath = App.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
storageDir = new File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
} catch (IOException e) { 

e.printStackTrace();
}
//创建Uri
Uri  photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", imageFile );
代码语言:javascript
复制
com.hm.camerademo.fileprovider

要和在AndroidManifest.xml中指定的一样。不然会报错。

第四步 分享一个 Content URI 这个例子中我们是向系统的相机传递一个Uri

代码语言:javascript
复制
photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);
Log.e(TAG, photoURI.getPath());
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);

最后附上两张图,图片来自参考文档2

图一:使用Uri.fromFile()的方式生成一个Uri

这里写图片描述
这里写图片描述

图一:使用FileProvider.getUriForFile(this, “com.hm.camerademo.fileprovider”, photoFile);的方式生成一个Uri

这里写图片描述
这里写图片描述

参考链接:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/222862.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月29日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用FileProvider四部曲
  • 注意
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档