android 把file资源转file,Android文件管理器选择文件,获得文件路径URI转File

本文介绍了在Android中如何从文件管理器选择文件并转换URI为File,涉及MIME类型、文件路径转URI、指定浏览位置、设置多种文件类型以及处理QQ浏览器的特殊问题。详细讲解了处理过程中可能遇到的坑和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记一次文件上传引发的血案。

解决QQ浏览器com.tencent.mtt.fileprovider问题。

更新列表

日期

修改内容

2019年7月2日

更新遇到的问题

前情描述:

使用系统文件管理器,选择指定文件类型上传。

基础知识

MIME

调起文件管理器

指定浏览位置(路径转URI)

设置多种文件类型

URI转路径

踩坑

com.tencent.mtt.fileprovider 问题

1. MIME

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。

final String DOC = "application/msword";

final String XLS = "application/vnd.ms-excel";

final String PPT = "application/vnd.ms-powerpoint";

final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

final String XLSX = "application/x-excel";

final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";

final String PDF = "application/pdf";

final String MP4 = "video/mp4";

final String M3U8 = "application/x-mpegURL";

更多文件类型,自行百度

2. 调起文件管理器

所有类型文件

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

//任意类型文件

intent.setType("*/*");

intent.addCategory(Intent.CATEGORY_OPENABLE);

startActivityForResult(intent,1);

//-------常用类型

//图片

//intent.setType(“image/*”);

//音频

//intent.setType(“audio/*”);

//视频

//intent.setType(“video/*”);

//intent.setType(“video/*;image/*”);

系统的相冊

Intent intent= new Intent(Intent.ACTION_PICK, null);

intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");

startActivityForResult(intent, REQUEST_CODE_FILE);

3. 指定浏览位置(路径转URI)

跳转到指定路径下,涉及到将路径转为URI,考虑Android版本区别

/**

* file --> uri

* @param context

* @param file

*

* @return

*/

public static Uri getUriFromFile(Context context, File file) {

if (context == null || file == null) {

throw new NullPointerException();

}

Uri uri;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

uri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID + ".fileprovider", file);

} else {

uri = Uri.fromFile(file);

}

return uri;

}

由于7.0的升级还需要在AndroidManifest.xml中配置FileProvider

android:name="android.support.v4.content.FileProvider"

android:authorities="${applicationId}.fileprovider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/file_paths" />

${applicationId}.fileprovider这个配置要记牢,后期遇到大坑就靠这个值了。

xml/file_paths 文件如下:

将文件路径转(使用微信下载目录做测试)为URI后,设置到Intent中。

/**

* 根据类型,加载文件选择器

*/

private void chooseFileWithPath() {

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

intent.addCategory(Intent.CATEGORY_OPENABLE);

//跳转指定路径,如果路径不存在,切到sdcard

//需要读权限

String path = getSDPath();

if (!TextUtils.isEmpty(path)) {

//使用微信下载目录测试

path = path + File.separator + "tencent/MicroMsg/Download";

File file = new File(path);

if (file.exists()) {

//主要代码

intent.setDataAndType(FileUtil.getUriFromFile(this, new File(path)), "*/*");

} else {

intent.setType("*/*");

}

} else {

intent.setType("*/*");

}

startActivityForResult(intent, REQUEST_CODE_FILE);

}

主要生效代码:

Intent intent = new Intent(action,uri);

intent.setType("*/*");

startActivityForResult(intent, REQUEST_CODE_FILE);

或者

Intent intent = new Intent(action);

intent.setDataAndType(uri, "*/*");

startActivityForResult(intent, REQUEST_CODE_FILE);

特别注意:

指定目录这种方式调起,在原生的管理器没有其他注意的,但是如果用户使用三方的管理器,例如QQ浏览器,那么在进入到指定目录下,不可以执行返回操作。

例如:

如果路径设置的是/sdcard/tencent/MicroMsg/Download,在进入download目录下,如果该目录下没有文件,那么想返回到其上层目录MicroMsg,是不可以的。

4. 设置多种文件类型

通过intent.setType()方式设置的文件类型,只能生效一次,所以如果想只选择doc、excel、pdt、ppt等办公文档,过滤掉其他文件,就不能使用intent .setType()方式,而是使用Intent.EXTRA_MIME_TYPES来传值。

final String DOC = "application/msword";

final String XLS = "application/vnd.ms-excel";

final String PPT = "application/vnd.ms-powerpoint";

final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

final String XLSX = "application/x-excel";

final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";

final String PDF = "application/pdf";

/**

* 多种文件类型

*/

private void chooseMoreTypes() {

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

intent.addCategory(Intent.CATEGORY_OPENABLE);

String[] mimeTypes = {DOC, DOCX, PDF, PPT, PPTX, XLS, XLS1, XLSX};

intent.setType("application/*");

intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);

startActivityForResult(intent, REQUEST_CODE_FILE);

}

以上是准备工作,调起文件管理器进行选择文件,对于一个Android开发者来说,以上步骤只是相当于一小步,不要忘记适配。

接下来才是长征路。

5. URI转路径

在这一步主要解决的是将返回的URI转换为File,大坑也往往就在这一步出现。

从网上能找到File转File的代码,但是百度出来的内容,10篇有8篇是一样的,剩下2篇不能看。

但是这8篇中几乎都是相同的,没有解决一个至关重要的问题QQ文件管理器。

也可能是不会用搜索引擎吧。

上代码

/**

* 专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使

*/

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

public static String getPath(final Context context, final Uri uri) {

final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

//一些三方的文件浏览器会进入到这个方法中,例如ES

//QQ文件管理器不在此列

if (isExternalStorageDocument(uri)) {

final String docId = DocumentsContract.getDocumentId(uri);

final String[] split = docId.split(":");

final String type = split[0];

if ("primary".equalsIgnoreCase(type)) {

return Environment.getExternalStorageDirectory() + "/" + split[1];

}

}

// DownloadsProvider

else if (isDownloadsDocument(uri)) {

...

}

// MediaProvider

else if (isMediaDocument(uri)) {

...

}

} else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)

return getDataColumn(context, uri, null, null);

} else if ("file".equalsIgnoreCase(uri.getScheme())) {// File

...

}

return null;

}

发现部手机会有第三方的文件管理器,如ES,QQ等目前只接触到这两种,不排除其他APP,相信大部分都是好同志,但不限于鹅厂大佬。

通过QQ浏览器选择到的文件,则会报出不存在_data字段这个错误。

WTF

懵逼.jpg

返回的URI中uri.getAuthority()的值并不是自己在Manifest.xml中设置的${applicationId}.fileprovider而是com.tencent.mtt.fileprovider。

这时候,前面搜索到的文章,几乎都没有解决这个问题。

鉴于水平有限,被坑了半天后。

通过分析uri.getPath();的值。

content://com.tencent.mtt.fileprovider/QQBrowser/demo.mp4

写下来如下代码,如各位大佬有更好的解决方案,望转告。

//判断QQ文件管理器

if (isQQMediaDocument(uri)) {

String path = uri.getPath();

File fileDir = Environment.getExternalStorageDirectory();

File file = new File(fileDir, path.substring("/QQBrowser".length(), path.length()));

return file.exists() ? file.toString() : null;

}

测试通过机型: 魅族15,三星9300,mi6,oppoR9, 华为mate9

缺失代码自行补齐,

主要类代码FileUtil

其他问题

1. 文件类型设置

产品需求:

╔══════════════════════════════

║ 上传文件 上传视频

╚══════════════════════════════

要求: 1. 点击视频选择mp4,2. 点击文件选择pdf、word等office文件。

在设置Intent的时候分为2种:

intent.setType("*/*")

这种情况下,本意是想选择office文档。通过Intent.EXTRA_MIME_TYPES来限制文件类型,但是这种情况下,会出现第三方的文件管理器,而三方的一些情况下不会生效,所有文件都可以选择。

我做的是,通过观察MIME类型,我设置的是application/*第三方的文件管理图标隐藏掉了,只能通过系统的文件管理选择文件。

df60c12d27b6

设置"*/*"

df60c12d27b6

设置"application/*"

intent.setType("video/mp4);

这种会显示三方文件管理器,但是会过滤掉其他的文件,只有video类型的,如果有avi类型,那么还需要在onActivityResult中判断文件后缀名。

系统的文件管理器会生效,只能选择Intent.EXTRA_MIME_TYPES设置的类型。

2. 返回URI的问题

从文件管理器选择文件,返回的URI是content://com.android.externalstorage.documents/document/primary/update/A5679B1.mp4

从『视频』选择文件,返回的URI是content://com.android.providers.media.documents/document/video:5188

遇到的问题:

判断文件格式是否是我设置的类型,如果intent.setType("video/*");,但是只想要"mp4"格式的文件,那么在onActivityResult中通过返回的数据进行判断,前期想的是通过uri.getLastPathsegment(),判断文件的后缀名,但是后来测试遇到了第二种情况,从『视频』里选择到文件,这时返回URI不符合规则了,所以偷懒是不行的,只能通过转换,将源文件的名称,判断后缀名。

df60c12d27b6

选择视频

欢迎留言提问,共同进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值