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

Android中如何合规读取外部存储视频文件信息?

Android存储权限与文件访问合规解决方案

问题场景代码

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    private List<AssistInfo> assistInfos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String filePath1 = "/storage/emulated/0/Movies/RoveDashCam/Rove-R24K-PRO/Videos/2023_0327_174047_0005.MP4";
        String filePath2 = getFilesDir().getPath()+"/2023_0322_205825_0019.MP4";
        if(new File(filePath1).exists()){
            assistInfos = MP4Help.checkPlatform(filePath1);
            for (int i = 0; i <assistInfos.size() ; i++) {
                AssistInfo assistInfo = assistInfos.get(i);
                Log.d(TAG, assistInfo.getGps_lat()+","+assistInfo.getGps_lgt()+","+assistInfo.getSpd());
            }
        }
    }
}

问题背景

上述代码中,filePath1为外部存储公共目录路径,filePath2为应用内部私有存储路径:

  • 使用filePath1时,即便申请了常规外部存储读写权限,也无法通过指定的MP4Help库获取文件信息;仅申请MANAGE_EXTERNAL_STORAGE权限可正常访问,但该权限会导致应用被Google Play商店拒绝。
  • 使用filePath2无需任何权限即可正常访问文件内容。

技术解决方案

1. 合规获取外部存储中的文件信息

针对不同场景采用对应合规方案:

  • 应用私有外部存储:文件存放在getExternalFilesDir()或getExternalCacheDir()目录下时,无需额外权限,直接通过File类访问即可,此区域属于应用私有范围,系统允许直接操作。
  • 公共媒体目录文件:
    • Android 13+:申请对应媒体类型权限(如READ_MEDIA_VIDEO),即可通过Media Store API查询或访问视频文件;
    • Android 10~12:申请READ_EXTERNAL_STORAGE权限,该权限为常规权限,不会触发Google Play审核拒绝;
  • 非媒体类公共文件:引导用户通过系统文件选择器(ACTION_OPEN_DOCUMENT)获取文件URI,再通过ContentResolver访问文件内容,无需申请存储权限。

2. 读写内部存储中的大文件

内部存储为应用私有目录,无需权限,处理大文件核心是避免内存溢出,采用流式操作:

  • 流式读取:使用BufferedInputStream逐块读取文件,示例代码:
public void readLargeFile(String filePath) {
    try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath))) {
        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = bis.read(buffer)) != -1) {
            // 处理读取到的字节块,比如解析MP4信息
        }
    } catch (IOException e) {
        Log.e(TAG, "Read file error", e);
    }
}
  • 流式写入:使用BufferedOutputStream逐块写入,避免一次性加载大文件到内存;
  • 随机读写:若只需操作文件部分内容,使用RandomAccessFile定位到指定位置读写,节省内存开销。

3. 通过Media Store API读取文件信息(无需特殊权限)

通过Media Store API可合规访问公共媒体文件,无需MANAGE_EXTERNAL_STORAGE权限,步骤如下:

  1. 查询媒体文件:通过ContentResolver查询对应媒体类型的URI(视频文件用MediaStore.Video.Media.EXTERNAL_CONTENT_URI),获取目标文件的ContentUris;
  2. 获取文件流:通过ContentResolver.openInputStream()获取文件输入流,若MP4Help库仅支持文件路径,可将流复制到应用内部临时文件,再传入临时文件路径处理;
    示例代码:
public void queryVideoInfo(Context context, String fileName) {
    String[] projection = {
        MediaStore.Video.Media._ID,
        MediaStore.Video.Media.DISPLAY_NAME
    };
    String selection = MediaStore.Video.Media.DISPLAY_NAME + " = ?";
    String[] selectionArgs = {fileName};

    Cursor cursor = context.getContentResolver().query(
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
    );

    if (cursor != null && cursor.moveToFirst()) {
        long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID));
        Uri videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
        
        // 获取文件流并复制到内部临时文件
        try (InputStream is = context.getContentResolver().openInputStream(videoUri)) {
            File tempFile = new File(context.getCacheDir(), "temp.mp4");
            try (OutputStream os = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
            }
            // 用临时文件路径调用MP4Help
            assistInfos = MP4Help.checkPlatform(tempFile.getAbsolutePath());
            // 处理完成后删除临时文件
            tempFile.delete();
        } catch (IOException e) {
            Log.e(TAG, "Process video error", e);
        }
        cursor.close();
    }
}
  • 若用户通过文件选择器选择文件,直接使用返回的URI调用openInputStream()即可,无需申请任何存储权限。

内容的提问来源于stack exchange,提问作者MARSH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 12:42:32