Android DownloadManager и FileProvider - PullRequest
       66

Android DownloadManager и FileProvider

0 голосов
/ 30 июня 2018

Я хочу использовать DownloadManager для Android, чтобы загрузить PDF, а затем дать пользователю возможность открыть его с помощью приложения для просмотра PDF.

Для этого я сохраняю инициацию загрузки, используя следующее:

public String downloadFile(String url, String name) {
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setDescription("Downloading file: " + name);
        request.setTitle("My app name");
        request.allowScanningByMediaScanner();
     request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setMimeType("application/pdf");
        Uri destinationUri = Uri.fromFile(new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), name+".pdf"));
        request.setDestinationUri(destinationUri);
        manager.enqueue(request);
        return destinationUri.toString();
    }

Я сохраняю Uri и использую его для открытия PDF-файла с помощью приложения для просмотра PDF, например:

public void openDownloadedFile(String uri) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    File file = new File(uri);
    Uri uriForFile = FileProvider.getUriForFile(context.getApplicationContext(), context.getString(R.string.my_file_authority), file);
    intent.setDataAndType(uriForFile, "application/pdf");
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    context.startActivity(intent);
}
Файл манифеста

My application содержит следующий поставщик:

        <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="@string/my_file_authority"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

И у меня также есть следующее содержание в provider_paths:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path
        name="my_pdfs"
        path="."/>
    <external-path
        name="my_pdfs"
        path="."/>
    <files-path
        name="my_pdfs"
        path="."/>
</paths>

Я знаю, что там есть 2 дополнительных элемента, но я просто хотел убедиться, что я ничего не пропустил.

К сожалению, это происходит сбой по следующей причине:

java.lang.IllegalArgumentException: Failed to find configured root that contains /file:/storage/emulated/0/Android/data/{my_app_id}/files/Download/filename_of_pdf.pdf
    at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:738)
    at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:417)

Некоторые дополнительные сведения:

minSdkVersion 21
targetSdkVersion 27

Я тестирую на эмуляторе Samsung S8 и Android с Android O.

Может кто-нибудь дать мне подсказку о том, почему это происходит? Как я могу узнать, что Android рассматривает мои provider_paths?

Спасибо!

PS:

  • Я пытался использовать внешний публичный каталог для DownloadManager, но он вылетает, говоря, что у него нет разрешений, даже если в моем манифесте у меня есть разрешение WRITE_EXTERNAL_STORAGE, и я спрашиваю разрешение для пользователя, но диалоговое окно не появляется .
  • Я также заменил "." из файла provider_paths.xml с помощью «Скачать» или «Android» или даже «Android / data / {my_app_id} / files / Download /», но я получил тот же сбой

Ответы [ 2 ]

0 голосов
/ 27 июля 2019

Я немного опоздал на вечеринку здесь, но, видя, что я тоже с этим борюсь, я подумал, что смогу найти решение, которое я наконец нашел: после долгих раскопок я обнаружил, что мы должны осторожнее с file:// и content://. Оба используются как Uris, но первый обращается к файлу публично (подход DownloadManager), а другой - через разрешения (подход FileProvider).

Я подведу итог, как я это сделал. В моем случае я использую загруженный файл в приложении (извлечение метаданных из mp3 и последующее воспроизведение). Я включил то, что сработало для получения файла для моего использования, так как здесь также было много пробных ошибок, чтобы выяснить, какой метод использовать для доступа к файлу. Надеюсь, таким образом я смогу помочь другим, которые также пытаются ориентироваться в этом лабиринте Уриса и разрешений.

Загрузка файла с помощью DownloadManager

 //set up the download manager and the file destination
 final DownloadManager dlManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
 String destination = Environment.getExternalStorageDirectory() + "*APPFOLDER*";

 //ensure the folder exists before continuing
 File file = new File(destination);
 if (!file.exists())
 file.mkdirs();

 destination += "/" + "*FILENAME*";
 final Uri destinationUri = Uri.parse("file://" + destination);

 //create the download request
 DownloadManager.Request dlRequest = new DownloadManager.Request(uri);
 dlRequest.setDestinationUri(destinationUri);
 final long dlId = dlManager.enqueue(dlRequest);

Доступ к файлу

File myPath = new File(Environment.getExternalStorageDirectory(), "*APPFOLDER*");
File myFile = new File(myPath, "*FILENAME*");
Uri myUri = FileProvider.getUriForFile(ctxt, ctxt.getApplicationContext().getPackageName() + ".file_provider", myFile);
ctxt.grantUriPermission(ctxt.getApplicationContext().getPackageName(), myUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

    MediaMetadataRetriever mmr = new MediaMetadataRetriever();
    mmr.setDataSource(ctxt, myUri);
0 голосов
/ 30 июня 2018
File file = new File(uri);

A Uri не является файлом.

java.lang.IllegalArgumentException: Failed to find configured root that contains /file:/storage/emulated/0/Android/data/{my_app_id}/files/Download/filename_of_pdf.pdf

/file:/storage/emulated/0/Android/data/{my_app_id}/files/Download/filename_of_pdf.pdf - это не путь к файловой системе на Android (ни AFAIK для любой другой операционной системы).

Чтобы это исправить:

  1. Удерживайте результат new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), name+".pdf") из вашего destinationUri экземпляра как File

  2. Используйте это File с getUriForFile()

...