You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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权限。

核心实现步骤

  1. 从URI中获取资源的原始文件名
    通过ContentResolver查询URI的MediaStore.MediaColumns.DISPLAY_NAME字段获取原始文件名,避免文件后缀丢失。
  2. 构造目标输出文件
    在你指定的临时目录下,用获取到的文件名创建空的输出文件,可自定义重名覆盖逻辑。
  3. 流拷贝完成文件写入
    通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:54:02