Android 11下如何通过URI复制文件实现卡片包资源跨设备分享
Android 11 下URI资源复制到指定目录实现方案
前置准备
- 确保你持有目标URI的读取权限:如果URI是通过系统文件选择器/相册选择器获取的,需要先调用
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)持久化读取权限,避免权限回收后无法读取资源。 - 目标目录建议优先使用应用专属目录:比如
context.filesDir下的子目录、context.cacheDir下的临时子目录,不需要申请额外存储权限;如果要写入公共下载目录,Android11及以上使用MediaStore.Downloads API即可,不需要申请MANAGE_EXTERNAL_STORAGE权限。
核心实现步骤
- 从URI中获取资源的原始文件名
通过ContentResolver查询URI的MediaStore.MediaColumns.DISPLAY_NAME字段获取原始文件名,避免文件后缀丢失。 - 构造目标输出文件
在你指定的临时目录下,用获取到的文件名创建空的输出文件,可自定义重名覆盖逻辑。 - 流拷贝完成文件写入
通过ContentResolver打开URI的输入流,和目标文件的输出流做流拷贝,完成资源复制。
示例代码(Kotlin)
import android.content.ContentResolver import android.content.Context import android.net.Uri import android.provider.OpenableColumns import java.io.File import java.io.FileOutputStream import java.io.InputStream fun copyUriToDirectory(context: Context, sourceUri: Uri, targetDir: File): File? { // 1. 获取资源文件名 val contentResolver = context.contentResolver var fileName: String? = null contentResolver.query(sourceUri, null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) if (nameIndex != -1) { fileName = cursor.getString(nameIndex) } } } // 降级规则:如果拿不到文件名,用uri最后一段+时间戳避免重名 if (fileName.isNullOrEmpty()) { fileName = "${sourceUri.lastPathSegment}_${System.currentTimeMillis()}" } // 2. 创建目标文件 val targetFile = File(targetDir, fileName!!) if (targetFile.exists()) { targetFile.delete() } targetFile.createNewFile() // 3. 流拷贝写入 var inputStream: InputStream? = null var outputStream: FileOutputStream? = null return try { inputStream = contentResolver.openInputStream(sourceUri) ?: return null outputStream = FileOutputStream(targetFile) val buffer = ByteArray(1024 * 8) var length: Int while (inputStream.read(buffer).also { length = it } > 0) { outputStream.write(buffer, 0, length) } outputStream.flush() targetFile } catch (e: Exception) { e.printStackTrace() null } finally { inputStream?.close() outputStream?.close() } }
调用示例
// 示例:创建缓存目录下的临时卡片导出目录 val tempCardDir = File(context.cacheDir, "card_export_temp") if (!tempCardDir.exists()) { tempCardDir.mkdirs() } // 调用复制方法,yourMediaUri替换为你实际的媒体资源URI val copiedFile = copyUriToDirectory(context, yourMediaUri, tempCardDir) // 复制成功后即可修改XML内的资源路径为copiedFile的相对路径,后续压缩整个tempCardDir即可
内容的提问来源于stack exchange,提问作者user16298059
相关产品推荐
相关产品推荐

