已获权限仍在Android SD卡创建文件时触发IO异常问题咨询
解决Android中将文件从内部存储移动到SD卡时的创建文件IO异常问题
你遇到的这个IO异常,核心原因是Android 10(API 29)及以上版本的Scoped Storage机制限制了对可移除SD卡非私有目录的直接文件路径访问——哪怕你申请了传统的存储权限也不行。下面我来拆解问题并给出具体解决方案:
为什么直接用File类操作SD卡目录会失败?
- 从Android 10开始,系统引入了Scoped Storage,App只能直接访问自身的私有目录(比如
/storage/emulated/0/Android/data/你的包名/)和通过MediaStore访问媒体文件。对于可移除SD卡的自定义目录(比如你代码里的/storage/48EE-C144/chosenDirectory/),直接用File类的绝对路径进行写入操作会被系统拒绝。 - 你获取的
WRITE_EXTERNAL_STORAGE权限在Android 10+已经不再授予对外部存储全局写入的能力,仅能访问媒体文件和App私有目录。
正确的解决方案:使用Storage Access Framework(SAF)
要写入SD卡的自定义目录,你需要通过SAF让用户手动选择目标目录,获取该目录的访问权限,然后通过ContentResolver来创建文件和复制内容,而不是直接用File类。
步骤1:启动SAF让用户选择目标目录
private val REQUEST_CODE_PICK_DIRECTORY = 1001 fun pickTargetDirectory() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) startActivityForResult(intent, REQUEST_CODE_PICK_DIRECTORY) }
步骤2:处理选择结果,保存目录的持久权限
在onActivityResult(或Jetpack的registerForActivityResult)中获取选中目录的Uri,并申请持久访问权限:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_PICK_DIRECTORY && resultCode == Activity.RESULT_OK) { data?.data?.let { treeUri -> // 申请持久访问权限,重启App后依然有效 contentResolver.takePersistableUriPermission( treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) // 保存treeUri,比如存入SharedPreferences,后续用来操作目录 saveTreeUriToPrefs(treeUri.toString()) // 调用文件移动方法 moveFileToTargetDirectory(treeUri, oldPath) } } }
步骤3:通过ContentResolver创建文件并复制内容
fun moveFileToTargetDirectory(treeUri: Uri, oldFilePath: String) { val oldFile = File(oldFilePath) if (!oldFile.exists()) return // 目标文件名,和原文件名一致 val fileName = oldFile.name // 在选中的目录下创建新文件 val docUri = DocumentsContract.createDocument( contentResolver, treeUri, "application/octet-stream", // 根据你的文件类型修改MIME类型 fileName ) ?: return // 复制文件内容 contentResolver.openOutputStream(docUri)?.use { outputStream -> oldFile.inputStream().use { inputStream -> inputStream.copyTo(outputStream) } } // 复制完成后删除原文件 oldFile.delete() }
额外注意事项
- 如果你需要支持Android 9及以下版本,可以保留原有的
File类操作逻辑,但要确保已经申请了WRITE_EXTERNAL_STORAGE动态权限。 - 不要硬编码SD卡的绝对路径(比如
/storage/48EE-C144/),因为不同设备的SD卡路径可能不同,SAF是唯一可靠的方式。
内容的提问来源于stack exchange,提问作者Michał Jabłoński
相关产品推荐
相关产品推荐

