Открытие файла с SD-карты через Intent.ACTION_VIEW - PullRequest
0 голосов
/ 17 мая 2018

Я знаю, что мы обычно можем открыть файл из внутреннего хранилища следующим образом:

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

Я прочитал так много сообщений в StackOverflow, но ничего не помогло.Можете ли вы помочь мне решить эту ошибку?Заранее спасибо.

Ответы [ 4 ]

0 голосов
/ 18 мая 2018

Я наконец нашел решение для этого.java.lang.SecurityException: Permission Denial будет выброшено при открытии внешнего файла с помощью DocumentFile.fromSingleUri(context, uri).Этот статический метод полезен только тогда, когда вы выбираете файл внутри onActivityResult() метода с действием Intent.ACTION_OPEN_DOCUMENT или Intent.ACTION_CREATE_DOCUMENT.Когда ваше приложение уничтожено, разрешение на доступ к файлу исчезает.Вы не должны использовать DocumentFile.fromSingleUri(context, uri) при работе с файлами, требующими длительного разрешения.Следовательно, нам нужно DocumentFile.fromTreeUri(context, uri).

Чтобы сделать это, вы должны получить доступ к корню ID карты SD (например, 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 становится хуже с каждым днем.Они создали Storage Access Framework (SAF), который затрудняет управление файлами на SD-карте.Использование java.io.File почти не рекомендуется с Android Kitkat.Вы должны использовать DocumentFile вместо.

0 голосов
/ 17 мая 2018

Вы должны добавить разрешение в файле манифеста, а для версии для Android с маршмеллоу или выше вы должны добавить разрешение во время выполнения. для этого вида

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

0 голосов
/ 17 мая 2018

Я получаю Uri, используя Intent.ACTION_OPEN_DOCUMENT. Затем я сохраняю Uri в своей базе данных. Наконец, я реконструирую Uri для получения потока, используя getContentResolver (). OpenInputStream (uri).

Я предполагаю, что вы не звоните takePersistableUriPermission() на ContentResolver в onActivityResult(), прежде чем сохранить Uri в базе данных. Без этого у вас не будет долгосрочного доступа к контенту, обозначенному Uri.

0 голосов
/ 17 мая 2018

Это может быть очень просто, но в сообщении об ошибке говорится, что вам не хватает разрешения

вы просили READ_EXTERNAL_STORAGE?

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

...