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

SDK23+下外置SD卡文件Content URI生成与视频打开失败问题

嘿,我来帮你搞定这个问题!你遇到的执行失败,核心原因是 FileProvider默认只支持访问应用私有目录、以及外部存储的公共目录(比如系统默认的Download、DCIM),完全没法直接碰外置SD卡(可移动存储)的目录。那个/storage/9C33-6BBD/属于二级外部存储,不在FileProvider预设的路径范围内,所以调用FileProvider.getUriForFile()肯定会出问题。

下面分Android版本给你对应的解决方案:

方案1:Android 10及以上(API 29+)用MediaStore获取URI

从Android 10开始的Scoped Storage机制,推荐通过MediaStore来查询文件的内容URI,这样既符合系统规范,又能正常访问外置SD卡的文件:

// 用异步任务避免阻塞主线程
new AsyncTask<Void, Void, Uri>() {
    @Override
    protected Uri doInBackground(Void... voids) {
        String targetFilePath = "/storage/9C33-6BBD/Downloads/video.mp4";
        // 查询需要的字段
        String[] projection = {MediaStore.Video.Media._ID};
        // 按文件路径匹配
        String selection = MediaStore.Video.Media.DATA + "=?";
        String[] selectionArgs = {targetFilePath};
        
        Cursor cursor = context.getContentResolver().query(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                null
        );
        
        Uri videoUri = null;
        if (cursor != null && cursor.moveToFirst()) {
            long videoId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID));
            videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId);
            cursor.close();
        }
        return videoUri;
    }

    @Override
    protected void onPostExecute(Uri uri) {
        if (uri != null) {
            Intent viewIntent = new Intent(Intent.ACTION_VIEW);
            viewIntent.setDataAndType(uri, "video/mp4");
            viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            context.startActivity(viewIntent);
        } else {
            Toast.makeText(context, "找不到目标视频或无访问权限", Toast.LENGTH_SHORT).show();
        }
    }
}.execute();

方案2:Android 6.0-9.0(API 23-28)用存储访问框架(SAF)

这个版本里FileProvider依旧不支持外置SD卡,最稳妥的方式是通过SAF让用户主动授权访问目标文件:

第一步:启动文件选择器

private static final int REQUEST_CODE_PICK_VIDEO = 1001;

// 启动SAF选择视频文件
Intent pickIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
pickIntent.addCategory(Intent.CATEGORY_OPENABLE);
pickIntent.setType("video/mp4");
// 可选:引导用户直接到SD卡的Download目录
Uri initialDirUri = Uri.parse("content://com.android.externalstorage.documents/document/9C33-6BBD%3ADownloads");
pickIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDirUri);
startActivityForResult(pickIntent, REQUEST_CODE_PICK_VIDEO);

第二步:处理选择结果

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_PICK_VIDEO && resultCode == RESULT_OK) {
        if (data != null) {
            Uri videoUri = data.getData();
            // 可选:持久化权限,后续不用再让用户授权
            getContentResolver().takePersistableUriPermission(videoUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
            
            Intent viewIntent = new Intent(Intent.ACTION_VIEW);
            viewIntent.setDataAndType(videoUri, "video/mp4");
            viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(viewIntent);
        }
    }
}

额外注意事项

  • 不管哪个版本,都需要申请READ_EXTERNAL_STORAGE权限,API 23+还要记得动态申请;
  • Android 11+(API 30)如果想直接硬编码路径访问外置SD卡,还得申请MANAGE_EXTERNAL_STORAGE权限,但这个权限需要在应用商店说明用途,一般不推荐用;
  • 尽量别硬编码SD卡路径,不同设备的挂载路径可能不一样,优先用系统API或SAF动态获取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:33:22