通过Intent.ACTION_VIEW从SD卡打开文件

时间:2018-05-17 11:34:43

标签: android android-intent android-sdcard

我知道我们可以正常从内部存储打开文件,如下所示:

File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "my_file.zip");
Uri uri = (FileProvider.getUriForFile(context, AUTHORITY_OPEN_FILE, file)
Intent intent = new Intent(Intent.ACTION_VIEW)
            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .setData(uri);
context.startActivity(intent);

但我不知道如何从SD卡打开文件,我们可以使用DocumentFile这样选择:

Uri uri = new Uri.Builder()
            .scheme("content")
            .authority("com.android.externalstorage.documents")
            .appendPath("document")
            .appendPath(directory)
            .appendPath(fileName)
            .build();
DocumentFile documentFile = DocumentFile.fromSingleUri(context, uri);

我尝试了以下代码段:

Uri uri = documentFile.getUri();
Intent intent = new Intent(Intent.ACTION_VIEW)
            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .setData(uri);
context.startActivity(intent);

但结果是错误:

 Caused by: java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.VIEW dat=content://com.android.externalstorage.documents/document/6331-6132:/haxm-windows_r05_2.zip flg=0x10000001 cmp=com.google.android.gm/.browse.TrampolineActivity } from ProcessRecord{a0ed6d5 9894:com.mypackage.app/u0a169} (pid=9894, uid=10169) requires com.google.android.gm.permission.READ_GMAIL

我确实授予了读写外部存储权限:

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

还有Uri权限:

int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
context.getContentResolver().takePersistableUriPermission(uri, takeFlags);

存储在SD卡中的文件为我们提供Uri6331-6132是我们可移动SD卡的标识符:

content://com.android.externalstorage.documents/document/6331-6132:Folder/haxm-windows_r05_2.zip

我在StackOverflow上阅读了很多帖子,但没有任何帮助。你能帮我解决这个错误吗?提前谢谢。

4 个答案:

答案 0 :(得分:0)

这可能是非常基本的,但错误消息表明您缺少权限

你问READ_EXTERNAL_STORAGE了吗?

https://developer.android.com/training/data-storage/files#ExternalStoragePermissions

答案 1 :(得分:0)

你必须在清单文件中添加权限,对于marshmallow或以上版本的android你添加运行时权限。 对于这个观点

https://www.simplifiedcoding.net/android-marshmallow-permissions-example/

答案 2 :(得分:0)

  

我使用Intent.ACTION_OPEN_DOCUMENT获取Uri。接下来,我将Uri保存到我的数据库中。最后,我使用getContentResolver()。openInputStream(uri)重建Uri来获取流​​。

我的猜测是,在将ContentResolver保存到数据库之前,您没有在onActivityResult()中的Uri上调用test.route。没有这个,you do not have long-term access to the content identified by the Uri

答案 3 :(得分:0)

我终于找到了解决方案。使用java.lang.SecurityException: Permission Denial打开外部文件时,将引发DocumentFile.fromSingleUri(context, uri)。只有当您使用操作onActivityResult()Intent.ACTION_OPEN_DOCUMENTIntent.ACTION_CREATE_DOCUMENT方法中选择文件时,此静态方法才有用。当您的应用被销毁时,访问该文件的权限就会消失。在处理需要很长时间权限的文件时,不应使用DocumentFile.fromSingleUri(context, uri)。因此,我们需要DocumentFile.fromTreeUri(context, uri)

为此,您必须首先访问SD卡ID的根目录(例如6331-6132)。例如:

String path = "6331-6132:Video/Iykwim.mp4";
String sdcardId = path.substring(0, path.indexOf(':') + 1); // => returns '6331-6132:'
Uri uri = Uri.parse("content://com.android.externalstorage.documents/tree/" + Uri.encode(sdcardId));
DocumentFile root = DocumentFile.fromTreeUri(context, uri);

// Now, we already have the root of SD card.
// Next, we will get into => Video => Iykwim.mp4
DocumentFile file = root.findFile("Video").findFile("Iykwim.mp4");

请注意,无法直接访问该文件,例如Uri

String path = "6331-6132:Video/Iykwim.mp4";
Uri uri = Uri.parse("content://com.android.externalstorage.documents/tree/" + Uri.encode(path));
DocumentFile file = DocumentFile.fromTreeUri(context, uri);

或者像这样:

String folder = "6331-6132:Video";
Uri uri = Uri.parse("content://com.android.externalstorage.documents/tree/" + Uri.encode(folder));
DocumentFile directoryVideo = DocumentFile.fromTreeUri(context, uri);
DocumentFile file = directoryVideo.findFile("Iykwim.mp4");

最后,您可以使用Intent.ACTION_VIEW打开文件:

Intent intent = new Intent(Intent.ACTION_VIEW)
            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .setData(documentFile.getUri());
context.startActivity(intent);

最糟糕的情况是当你从里面有这么多文件的文件夹中调用findFile()时。这将需要很长时间,并可能导致您的应用程序崩溃。确保始终从不同的线程调用此方法。

Android日益恶化。他们制作了一个存储访问框架(SAF),使您难以管理SD卡上的文件。自Android Kitkat以来,使用java.io.File几乎已被弃用。您应该使用DocumentFile代替。