Android中使用MediaStore.Video保存视频至图库的方案咨询及代码实现
Android视频压缩后存入图库方案分析与实现
现有方案合理性分析
你当前的方案在Android 10(API29)及以下版本可以正常工作,但在Android 11(API30)及以上版本会出现兼容性问题:
- API30+强制启用Scoped Storage,
WRITE_EXTERNAL_STORAGE权限不再生效,requestLegacyExternalStorage=true会被系统忽略,应用无法直接通过文件路径写入公共Movies目录 - 直接操作
Environment.getExternalStoragePublicDirectory返回的路径,在API30+会触发权限异常,导致视频无法保存
现有方案代码&权限配置
核心代码
public String getVideoFilePath() { return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath() + "/" + System.currentTimeMillis() + "compress.mp4"; } public static void exportMp4ToGallery(Context context, String filePath) { File file = new File(filePath); MediaScannerConnection.scanFile(context, new String[]{file.toString()}, null, null); }
权限配置
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/> android:requestLegacyExternalStorage="true"
是否应该改用MediaStore.Video?
必须改用MediaStore.Video,原因如下:
- 适配Android 11+的Scoped Storage规范,API30+无需申请
WRITE_EXTERNAL_STORAGE权限 - 官方推荐的共享媒体文件访问方式,兼容性覆盖所有Android版本
- 系统自动管理媒体文件索引,无需手动调用
MediaScannerConnection.scanFile
MediaStore.Video实现直接写入公共图库的代码示例
以下代码直接将压缩后的视频写入系统公共Movies目录,无需先存应用私有目录:
/** * 将压缩后的视频写入系统公共Movies目录,返回对应Uri * @param context 上下文 * @param compressedVideoPath 压缩后视频的本地路径(建议存应用私有目录) * @return 写入MediaStore后的Uri,失败返回null */ public Uri saveCompressedVideoToGallery(Context context, String compressedVideoPath) { ContentResolver resolver = context.getContentResolver(); String videoFileName = "compress_" + System.currentTimeMillis() + ".mp4"; ContentValues values = new ContentValues(); // 设置视频元数据 values.put(MediaStore.Video.Media.TITLE, videoFileName); values.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName); values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000); values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis()); Uri collectionUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 用RELATIVE_PATH指定存储位置 values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES); collectionUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { // Android 9及以下,直接指定文件路径 String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath(); File videoFile = new File(directory, videoFileName); values.put(MediaStore.Video.Media.DATA, videoFile.getAbsolutePath()); collectionUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } // 插入元数据,获取写入Uri Uri savedVideoUri = resolver.insert(collectionUri, values); if (savedVideoUri == null) { return null; } // 写入视频文件内容 try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(savedVideoUri, "w"); FileOutputStream outputStream = new FileOutputStream(pfd.getFileDescriptor()); FileInputStream inputStream = new FileInputStream(compressedVideoPath)) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // Android 10+ 标记文件为已完成 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { values.clear(); values.put(MediaStore.Video.Media.IS_PENDING, 0); resolver.update(savedVideoUri, values, null, null); } } catch (IOException e) { // 写入失败,删除已插入的元数据 resolver.delete(savedVideoUri, null, null); e.printStackTrace(); return null; } return savedVideoUri; }
使用说明
- 压缩类可先将视频保存到应用私有目录(比如
getExternalFilesDir("compressed")),此路径无需额外权限 - 调用上述方法传入压缩后的视频路径,即可完成写入
- API30+无需申请存储权限,API29及以下需动态申请
WRITE_EXTERNAL_STORAGE
关于“先存应用外部目录再复制到图库”方案的说明
你提到的该方案完全可行,适合以下场景:
- 需要保留视频的应用私有副本,删除应用时自动清理这些文件
- 压缩过程需要临时存储,避免占用公共目录空间
该方案核心逻辑没问题,可优化点:用ContentResolver.openOutputStream替代手动文件流复制,代码更简洁;API30+无需任何存储权限即可写入应用私有外部目录。
内容的提问来源于stack exchange,提问作者ThinQ
相关产品推荐
相关产品推荐

