通过 Intent.ACTION_VIEW 从 SD 卡打开文件
Opening file from SD card via Intent.ACTION_VIEW
我知道我们可以像这样从内部存储正常打开文件:
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 卡中的文件给我们一个 Uri
像这样,其中 6331-6132
是我们可移动 SD 卡的标识符:
content://com.android.externalstorage.documents/document/6331-6132:Folder/haxm-windows_r05_2.zip
我在 Whosebug 上阅读了很多帖子,但没有任何帮助。你能帮我解决这个错误吗?提前致谢。
这可能是非常基本的,但错误消息说您缺少权限
你问的是 READ_EXTERNAL_STORAGE
吗?
https://developer.android.com/training/data-storage/files#ExternalStoragePermissions
你必须在清单文件中添加权限,对于 marshmallow 或更高版本的 android 你添加 运行 时间权限。
对于此视图
https://www.simplifiedcoding.net/android-marshmallow-permissions-example/
I get the Uri using Intent.ACTION_OPEN_DOCUMENT. Next, I save the Uri into my database. Finally, I reconstruct the Uri to get stream using getContentResolver().openInputStream(uri).
我的猜测是,在将 Uri
保存到数据库之前,您没有在 onActivityResult()
中的 ContentResolver
上调用 takePersistableUriPermission()
。没有这个,you do not have long-term access to the content identified by the Uri
.
我终于找到了解决办法。当您使用 DocumentFile.fromSingleUri(context, uri)
打开外部文件时将抛出 java.lang.SecurityException: Permission Denial
。此静态方法仅当您在 onActivityResult()
方法中选择文件时才有用,操作为 Intent.ACTION_OPEN_DOCUMENT
或 Intent.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
。
我知道我们可以像这样从内部存储正常打开文件:
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 卡中的文件给我们一个 Uri
像这样,其中 6331-6132
是我们可移动 SD 卡的标识符:
content://com.android.externalstorage.documents/document/6331-6132:Folder/haxm-windows_r05_2.zip
我在 Whosebug 上阅读了很多帖子,但没有任何帮助。你能帮我解决这个错误吗?提前致谢。
这可能是非常基本的,但错误消息说您缺少权限
你问的是 READ_EXTERNAL_STORAGE
吗?
https://developer.android.com/training/data-storage/files#ExternalStoragePermissions
你必须在清单文件中添加权限,对于 marshmallow 或更高版本的 android 你添加 运行 时间权限。 对于此视图
https://www.simplifiedcoding.net/android-marshmallow-permissions-example/
I get the Uri using Intent.ACTION_OPEN_DOCUMENT. Next, I save the Uri into my database. Finally, I reconstruct the Uri to get stream using getContentResolver().openInputStream(uri).
我的猜测是,在将 Uri
保存到数据库之前,您没有在 onActivityResult()
中的 ContentResolver
上调用 takePersistableUriPermission()
。没有这个,you do not have long-term access to the content identified by the Uri
.
我终于找到了解决办法。当您使用 DocumentFile.fromSingleUri(context, uri)
打开外部文件时将抛出 java.lang.SecurityException: Permission Denial
。此静态方法仅当您在 onActivityResult()
方法中选择文件时才有用,操作为 Intent.ACTION_OPEN_DOCUMENT
或 Intent.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
。