You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:54:03