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"
|
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 baseDir = resolveBaseDir(context, destination)
|
||||||
val target = uniqueFile(baseDir, safeName)
|
val target = uniqueFile(baseDir, safeName)
|
||||||
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
|
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) {
|
private fun resolveBaseDir(context: Context, destination: FileDownloadDestination): File = when (destination) {
|
||||||
FileDownloadDestination.PublicDownloads -> {
|
FileDownloadDestination.PublicDownloads -> {
|
||||||
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户