【问题标题】:Android DownloadManager and FileProviderAndroid DownloadManager 和 FileProvider
【发布时间】:2018-06-30 16:44:48
【问题描述】:

我想使用 Android 的 DownloadManager 下载 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);
}

我的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

我正在使用 Android O 在三星 S8 和 Android 模拟器上进行测试。

有人可以告诉我为什么会这样吗? 我怎么知道 Android 正在考虑我的 provider_paths?

谢谢!

PS:

  • 我尝试为 DownloadManager 使用外部公共目录,但它崩溃了,说它没有权限,即使在我的清单中我确实有 WRITE_EXTERNAL_STORAGE 权限并且我请求用户的权限,但对话框没有出现.
  • 我也替换了“.”从带有“下载”或“Android”甚至“Android/data/{my_app_id}/files/Download/”的provider_paths.xml文件中,但我遇到了同样的崩溃

【问题讨论】:

    标签: android android-download-manager android-fileprovider


    【解决方案1】:

    我在这里聚会有点晚了,但看到我也在为此苦苦挣扎,我想我会提供我最终找到的解决方案:经过大量挖掘,我发现我们必须小心file://content://。两者都用作 Uris,但第一个公开访问文件(DownloadManager 方法),另一个通过权限访问文件(FileProvider 方法)。

    我会总结一下我最终是如何做到的。就我而言,我在应用程序中使用下载的文件(从 mp3 中提取元数据并稍后播放)。我已经包含了用于检索文件以供我使用的内容,就像这里一样,找出使用哪种方法来访问文件是很多试验错误。希望我可以因此帮助其他也在尝试导航这个 Uris 和权限迷宫的人。

    使用 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);
    

    【讨论】:

      【解决方案2】:
      File file = new File(uri);
      

      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. FilegetUriForFile() 一起使用

      【讨论】:

      • 嘿,非常感谢您抽出宝贵的时间,我试过了,我收到了同样的错误,但路径略有不同“ java.lang.IllegalArgumentException: 无法找到包含 /storage/ 的已配置根目录emulated/0/Android/data/{my_app_id}/files/Download/LFNZUZ61M3FSGZESBNDPBPPC.pdf”所以这次路径中缺少“file:/”。我在传递给 DownloadManager 的文件上使用 getUriForFile()。您认为这与我使用 getExternalFilesDir() 存储文件这一事实有关吗?
      • @Cata:尝试为 FileProvider 元数据 XML 资源中的三个元素使用不同的 name 值。或者,只需使用 &lt;external-files-path&gt; 元素并摆脱其他两个。三个具有相同的name 可能会混淆FileProvider
      • :( 就是这样,我还开始在 FileProvider 中添加一些断点,以查看它加载了什么配置,它只有一个。我重命名了每个元素,也只留下了 external-files-路径,它适用于两种情况。再次感谢您的帮助!
      猜你喜欢
      • 2017-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-24
      • 1970-01-01
      • 2016-10-07
      相关资源
      最近更新 更多