Android 10及以上版本如何获取共享存储文件夹路径?范围存储适配下支持isoparser元数据读写的路径解决方案咨询
适配Scoped Storage与isoparser库的解决方案
Great question! 确实,在Scoped Storage机制下,直接获取共享存储公共目录文件绝对路径的方式已经被系统限制,Environment.getExternalStoragePublicDirectory()这类方法也已废弃。不过我们可以通过ContentResolver + FileDescriptor的方式,完美适配依赖File对象的isoparser库,具体方案如下:
核心思路:用Uri和FileDescriptor替代File
Scoped Storage的核心是通过Uri来标识共享存储中的文件,再通过ContentResolver来获取文件的读写通道(FileDescriptor)。而isoparser库本身支持从InputStream/FileDescriptor/FileChannel创建IsoFile实例,完全不需要依赖绝对路径的File对象。
步骤1:获取共享存储中视频的Uri
首先需要通过MediaStore查询到目标视频对应的Uri,比如根据文件名查询:
private Uri getTargetVideoUri(Context context, String videoFileName) { String[] projection = {MediaStore.Video.Media._ID}; String selection = MediaStore.Video.Media.DISPLAY_NAME + " = ?"; String[] selectionArgs = {videoFileName}; Cursor cursor = context.getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { long videoId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)); cursor.close(); return ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId); } if (cursor != null) cursor.close(); return null; // 未找到对应文件 }
步骤2:修改元数据读取方法(适配Uri)
把原来依赖File的读取逻辑,改成用Uri打开FileDescriptor,再传入isoparser:
public static String readVideoMetadata(Context context, Uri videoUri) throws IOException { // 读取MP4元数据(isoparser逻辑) try (ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(videoUri, "r")) { if (pfd == null) { throw new IllegalStateException("无法打开文件描述符:" + videoUri); } try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) { IsoFile isoFile = new IsoFile(fis); try { AppleCommentBox commentBox = Path.getPath(isoFile, "moov[0]/udta[0]/meta[0]/ilst/©cmt"); if (commentBox != null) { return commentBox.getValue(); } } catch (OutOfMemoryError | Exception e) { e.printStackTrace(); } finally { isoFile.close(); } } } // 图片文件的Exif读取逻辑(同样适配Uri) try (InputStream inputStream = context.getContentResolver().openInputStream(videoUri)) { if (inputStream != null) { ExifInterface exifInterface = new ExifInterface(inputStream); String userComment = exifInterface.getAttribute(ExifInterface.TAG_USER_COMMENT); if (userComment != null) { return userComment; } } } catch (IOException e) { e.printStackTrace(); } return ""; }
步骤3:修改元数据写入方法(适配Uri)
写入逻辑同样通过ContentResolver打开可写的FileDescriptor,再用isoparser修改后写入:
public static void writeVideoMetadata(Context context, Uri videoUri, String metadata) throws IOException { try (ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(videoUri, "rw")) { if (pfd == null) { throw new IllegalStateException("无法打开可写文件描述符:" + videoUri); } FileChannel fileChannel = new FileInputStream(pfd.getFileDescriptor()).getChannel(); IsoFile isoFile = new IsoFile(fileChannel); // 创建或获取©cmt注释框 AppleCommentBox commentBox = Path.getPath(isoFile, "moov[0]/udta[0]/meta[0]/ilst/©cmt"); if (commentBox == null) { // 逐层创建缺失的Box结构(简化示例,实际需处理完整层级) MetaBox metaBox = Path.getPath(isoFile, "moov[0]/udta[0]/meta[0]"); IlmBox ilstBox = new IlmBox(); metaBox.addBox(ilstBox); commentBox = new AppleCommentBox(); ilstBox.addBox(commentBox); } // 设置元数据并写入文件 commentBox.setValue(metadata); try (FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor())) { isoFile.writeContainer(fos.getChannel()); fos.flush(); } finally { isoFile.close(); fileChannel.close(); } } }
额外注意事项
- 权限问题:如果是访问自己应用创建的视频文件,无需额外权限;如果是访问其他应用创建的视频,Android 10-12需要申请
READ_EXTERNAL_STORAGE权限,Android 13+可以使用更细粒度的READ_MEDIA_VIDEO权限。 - 文件创建:如果是要新创建视频文件并写入元数据,可以通过
MediaStore.Video.Media.insert()方法获取Uri,后续直接用该Uri操作即可。
内容的提问来源于stack exchange,提问作者Pulikese
相关产品推荐
相关产品推荐

