Android中访问CameraView录制视频路径及Kotlin最优视频存储方案咨询
你当前保存视频的路径是通过getExternalFilesDir(Environment.DIRECTORY_MOVIES)生成的应用专属外部存储目录,该目录默认仅你的应用自身有读写权限,系统自带的文件管理器默认不会展示应用专属目录的内容,所以你无法直接找到文件,访问方式如下:
- 应用内直接访问:直接使用你代码中拿到的
videoPath或videoUri即可完成读写操作,所有Android版本都不需要额外申请存储权限 - 调试阶段查看:打开Android Studio的Device File Explorer,进入路径
sdcard/Android/data/com.example.testgogoapplication/files/Movies即可找到对应视频,支持直接导出到本地电脑 - 系统文件管理器查看:将视频移动到公共媒体目录(如DCIM、公共Movies目录)后,即可在系统相册、文件管理器中正常显示
你贴出的原有创建文件代码如下:
@Throws(IOException::class) private fun createVideoFile(): File { // Create an image file name val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_MOVIES) return File.createTempFile( "oogoo_${timeStamp}_", ".mp4", storageDir ).apply { videoPath = this.path videoUri = this.toUri() } }
现有日志输出如下:
D/VideoTaken: /storage/emulated/0/Android/data/com.example.testgogoapplication/files/Movies/oogoo_20211021_125639_3062139219833544197.mp4
file:///storage/emulated/0/Android/data/com.example.testgogoapplication/files/Movies/oogoo_20211021_125639_3062139219833544197.mp4
这里注意你当前用了createTempFile创建临时文件,如果你需要长期留存视频,不要用临时文件创建方式,临时文件可能会被系统自动清理。
根据使用场景分为两种最优方案:
场景1:视频仅在你的应用内使用
直接使用应用专属外部存储方案,无需申请任何权限,兼容全Android版本,优化后的代码如下:
@Throws(IOException::class) private fun createAppPrivateVideoFile(): File { val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES) // 直接创建普通文件而非临时文件,避免被系统自动清理 val videoFile = File(storageDir, "oogoo_${timeStamp}.mp4") videoPath = videoFile.absolutePath videoUri = videoFile.toUri() return videoFile }
场景2:视频需要对外暴露,可在系统相册、其他应用中访问
使用MediaStore API适配分区存储规则,Android 10及以上不需要申请存储权限,Android 9及以下仅需申请WRITE_EXTERNAL_STORAGE权限,示例代码如下:
@RequiresApi(Build.VERSION_CODES.Q) private fun saveVideoToPublicDirectory(context: Context, sourceVideoFile: File): Uri? { val contentValues = ContentValues().apply { put(MediaStore.Video.Media.DISPLAY_NAME, "oogoo_${System.currentTimeMillis()}.mp4") put(MediaStore.Video.Media.MIME_TYPE, "video/mp4") put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/Oogoo") put(MediaStore.Video.Media.IS_PENDING, 1) // 标记文件正在写入,避免其他应用提前读取 } val contentResolver = context.contentResolver val targetUri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues) ?: return null // 将已生成的视频内容写入公共目录Uri contentResolver.openOutputStream(targetUri)?.use { outputStream -> FileInputStream(sourceVideoFile).use { inputStream -> inputStream.copyTo(outputStream) } } // 写入完成,更新文件状态为可访问 contentValues.clear() contentValues.put(MediaStore.Video.Media.IS_PENDING, 0) contentResolver.update(targetUri, contentValues, null, null) // 若不需要保留应用私有目录的副本,可在此处删除源文件 // sourceVideoFile.delete() return targetUri }
内容的提问来源于stack exchange,提问作者medmjs

