DownloadManager 无法使用 Pie 下载到三星的外部存储

Posted

技术标签:

【中文标题】DownloadManager 无法使用 Pie 下载到三星的外部存储【英文标题】:DownloadManager fails to download to external storage on Samsung with Pie 【发布时间】:2019-08-30 17:14:25 【问题描述】:

android 9 (Pie) 上的三星 Galaxy S9 和 S9+ 上,使用 Android 下载管理器将文件下载到外部存储失败。

下载适用于搭载 Android 8 的三星设备或其他搭载 Android 9 的设备(例如 Pixel 2)

我在清单中添加了以下权限:

 "android.permission.WRITE_EXTERNAL_STORAGE", 
 "android.permission.READ_EXTERNAL_STORAGE",
 "android.permission.INTERNET"

我还在运行时请求了 READ/WRITE_EXTERNAL_STORAGE 权限。

文件路径为: /storage/4295-DDD5/Android/data/com.example.myapplication/files/filename.file 并且这个文件路径存在,我是使用 AndroidStudio 的设备文件资源管理器创建的

创建下载的方法:

public void downloadFile(String url, String filePath) 
   DownloadManager mDownloadManager = 
   (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE);

   DownloadManager.Request request = new 
   DownloadManager.Request(Uri.parse(url));

   request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | 
   DownloadManager.Request.NETWORK_WIFI);

   request.setVisibleInDownloadsUi(true);

   request.setDestinationUri(Uri.parse("file://" + filePath));

   mDownloadManager.enqueue(request);

预期的结果是将指定文件从 url 下载到 filePath,而不是我在日志中收到以下错误:

D/DownloadManager: [8616] Starting com.example.myapplication
W/DownloadManager: [8616] Stop requested with status FILE_ERROR: Failed to generate filename: java.io.IOException: Permission denied
D/DownloadManager: [8616] Finished with status WAITING_TO_RETRY
V/DownloadManager: MIME Type = application/octet-stream
I/DownloadManager: Download 8616 finished with status WAITING_TO_RETRY
I/DownloadManager: Notification Clear Download 1:com.example.myapplication

【问题讨论】:

我在 PocketMaps 存储库中发现了同样的问题:github.com/junjunguo/PocketMaps/issues/104 我也可以在带有 Android Pie 的 Pocophone F1 上重现此错误。 嗨!您对此问题有任何消息\解决方案吗?我面临与 Galaxy Tab A 和 Pie 完全相同的问题。 DownloadManager 只是不写入 sd 卡,只写入内部,Environment.getExternalStorageDirectory() 也返回内部路径。但我的代码也适用于其他安卓设备。 【参考方案1】:

Oagar,过去帮助我的是阅读来自 https://commonsware.com/blog/archive.html 的所有 8 篇“外部存储之死”博文

我认为根本原因隐藏在这个:

文件路径是:/storage/4295-DDD5/Android/data/com.example.myapplication/files/filename.file 并且这个文件路径存在,我是使用AndroidStudio的设备文件资源管理器创建的

仅仅因为创建了这个目录并看到它并不意味着你的应用能够写入它。 所以试着在你知道的地方写,确保你可以写文件。

希望这会对你有所帮助。

【讨论】:

文件路径选择通过以下方法ContextCompat.getExternalFilesDirs(context, null);完成。错误在于 Android API 组件 DownloadManager 无法访问应用程序的“隔离存储沙箱”。如果我使用输入流:InputStream in = new URL(fileUrl).openStream(); Files.copy(in, Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING); 一切正常,文件下载到该位置。 ***.com/users/1911218/oagar 我遇到了类似的问题,使用下载管理器,需要将文件存储在 SD 卡上。在 Android Pie 上遇到同样的错误。你能解释一下你是如何解决你的问题的吗?可以提供足够的代码吗? 这个答案在这种情况下没有帮助。这似乎实际上是三星最新固件中的一个错误,未经任何许可或@Oagar 代码。我的应用程序面临同样的问题,该应用程序在多种设备和 Android 4 到 10 上运行良好......除了三星最新的 Android 9。【参考方案2】:

我设法通过使用setDestinationInExternalFilesDir (null 作为第二个参数)。

这应该将文件存储在与请求完全相同的位置,但实际实现似乎可以在三星设备上运行。我也没有请求任何许可 (*_EXTERNAL_STORAGE)。

【讨论】:

这是个好消息!我知道问题不在于权限,而在于目录位置。 嗨,hrach,我遇到了问题中提到的确切问题!您能否详细说明 null 第二个参数如何帮助您?另外,由于您将其发送为空,您如何为下载的文件设置名称? 嗨@Sagar,我现在使用的调用是setDestinationInExternalFilesDir(context, null, fileName) 文件名只是该方法选择的目录中的文件名。您可以通过 cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) 检索到的最终 URI @hranch 试用了request.setDestinationInExternalFilesDir(mContext, null, filePath);,它将文件放在此处:InternalStorage\Android\packageName\files\filepath 安装在搭载 Android 9 的三星 Galaxy S8 上。您还做过什么吗?问题是我们无法让 DownloadManager 将文件放在 ExternalStorage 上。【参考方案3】:

在你的 android 清单中添加这一行:

<application
    ...
    android:usesCleartextTraffic="true"/>

(更多信息在official doc)

【讨论】:

以上是关于DownloadManager 无法使用 Pie 下载到三星的外部存储的主要内容,如果未能解决你的问题,请参考以下文章

W/DownloadManager:中止下载请求 17:无法创建目标文件 /storage/emulated/0/Ringtone/Fav_Ringtone.mp3

无法让 CSS3Pie 工作

使用 DownloadManager 从 S3 下载 apk 文件

Downloadmanager实现app实现的升级下载使用

DownloadManager的使用

如何使用 DownloadManager 将下载的文件存储到应用程序分配的文件目录中?