API 29以下版本无法移动照片到指定文件夹的技术求助
API 29以下版本MediaStore保存照片后无法移动的问题解决
问题根源
你用File(Environment.getExternalStorageDirectory(), name)定位照片是错误的——通过MediaStore保存的照片,默认不会直接存在外部存储根目录,而是会被系统存到DCIM或Pictures这类默认媒体目录下,所以你创建的photo对象实际指向的是一个不存在的文件,renameTo自然返回false。
另外,API 29以下版本中,WRITE_EXTERNAL_STORAGE是危险权限,除了在Manifest中声明,还需要动态申请权限,如果没有获取到权限,文件操作也会失败。
解决方案
有两种可行的处理方式:
方式一:通过SavedUri获取真实路径后移动照片
通过ContentResolver查询savedUri对应的真实文件路径,再执行移动操作:
override fun onImageSaved(output: ImageCapture.OutputFileResults) { val savedUri = output.savedUri ?: return if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { // 1. 查询照片真实路径 val projection = arrayOf(MediaStore.Images.Media.DATA) requireContext().contentResolver.query(savedUri, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val realPath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)) val originalFile = File(realPath) // 2. 创建目标文件夹 val albumFolder = File( Environment.getExternalStorageDirectory(), "$appPicturesPath/$albumName" ) if (!albumFolder.exists()) { albumFolder.mkdirs() } // 3. 移动文件 val targetFile = File(albumFolder, originalFile.name) val isMoved = originalFile.renameTo(targetFile) if (isMoved) { // 4. 更新MediaStore数据库,避免媒体库中残留旧路径 val updateValues = ContentValues().apply { put(MediaStore.Images.Media.DATA, targetFile.absolutePath) } requireContext().contentResolver.update(savedUri, updateValues, null, null) Log.d(TAG, "照片移动成功") } else { Log.e(TAG, "照片移动失败") } } } } // 后续跳转、广播等代码保持不变 }
方式二:保存时直接指定目标路径(无需事后移动)
API 29以下可以直接通过MediaStore.Images.Media.DATA字段指定保存路径,一步到位:
private fun takePhoto() { val name = SimpleDateFormat(FILENAME, Locale.US).format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, PHOTO_TYPE) if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, "$appPicturesPath/$albumName") } else { // API29以下直接指定完整保存路径 val albumFolder = File( Environment.getExternalStorageDirectory(), "$appPicturesPath/$albumName" ) if (!albumFolder.exists()) { albumFolder.mkdirs() } val targetFile = File(albumFolder, name) put(MediaStore.Images.Media.DATA, targetFile.absolutePath) } } val outputOptions = ImageCapture.OutputFileOptions.Builder( requireContext().contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues ).build() // 后续拍照回调代码保持不变 }
注意事项
- 动态权限申请:API 23(Android 6.0)及以上版本,必须在代码中动态申请
WRITE_EXTERNAL_STORAGE权限,否则文件操作会被系统拒绝。 - MediaStore数据库更新:方式一中移动文件后,一定要更新MediaStore的记录,否则系统媒体库中会残留旧的文件路径,导致照片无法被正确检索。
内容的提问来源于stack exchange,提问作者koma222
相关产品推荐
相关产品推荐

