fix(file): 公共 Downloads 使用 MediaStore 写入,兼容华为设备
- Android 10+ 公共目录通过 MediaStore ContentResolver 写入 - 同时写入缓存文件供 openFile 使用 - 避免华为设备 mkdirs()/writeBytes() ENOENT 问题
这个提交包含在:
父节点
d2873eda66
当前提交
30ce89afcc
@ -251,7 +251,12 @@ object FileSDK {
|
||||
|
||||
val safeName = fileName.takeIf { it.isNotBlank() } ?: "download.bin"
|
||||
|
||||
// 尝试目标目录,如果 PublicDownloads 失败则回退到 Sandbox
|
||||
// Android 10+ 公共 Downloads 目录使用 MediaStore 写入(兼容华为等设备)
|
||||
if (destination == FileDownloadDestination.PublicDownloads && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
return saveBlobViaMediaStore(context, bytes, safeName)
|
||||
}
|
||||
|
||||
// 旧版本或 Sandbox 目录直接写文件
|
||||
val baseDir = resolveBaseDir(context, destination)
|
||||
val target = uniqueFile(baseDir, safeName)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
|
||||
@ -269,6 +274,44 @@ object FileSDK {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 MediaStore 将文件写入公共 Downloads 目录。
|
||||
* 在华为等设备上直接文件 I/O 会失败,MediaStore 通过 ContentResolver 操作,兼容性更好。
|
||||
* 返回缓存目录中的副本(真实文件,可被 openFile 使用)。
|
||||
*/
|
||||
private fun saveBlobViaMediaStore(context: Context, bytes: ByteArray, fileName: String): File {
|
||||
val mimeType = MimeTypeMap.getSingleton()
|
||||
.getMimeTypeFromExtension(fileName.substringAfterLast('.', "").lowercase())
|
||||
?: "application/octet-stream"
|
||||
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
||||
|
||||
// 写入 MediaStore
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Downloads.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Downloads.MIME_TYPE, mimeType)
|
||||
put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/$appName")
|
||||
put(MediaStore.Downloads.IS_PENDING, 1)
|
||||
}
|
||||
|
||||
val resolver = context.contentResolver
|
||||
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
|
||||
?: throw java.io.IOException("MediaStore insert returned null")
|
||||
|
||||
resolver.openOutputStream(uri)?.use { out ->
|
||||
out.write(bytes)
|
||||
} ?: throw java.io.IOException("Cannot open output stream for $uri")
|
||||
|
||||
values.clear()
|
||||
values.put(MediaStore.Downloads.IS_PENDING, 0)
|
||||
resolver.update(uri, values, null, null)
|
||||
|
||||
android.util.Log.d("XWV", "saveBlobViaMediaStore: saved $fileName, size=${bytes.size}, uri=$uri")
|
||||
|
||||
// 同时写入缓存目录,供 openFile 使用
|
||||
val cacheFile = File(context.cacheDir, fileName).apply { writeBytes(bytes) }
|
||||
return cacheFile
|
||||
}
|
||||
|
||||
private fun resolveBaseDir(context: Context, destination: FileDownloadDestination): File = when (destination) {
|
||||
FileDownloadDestination.PublicDownloads -> {
|
||||
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户