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

Android 13下载文件夹文件读取EACCES权限问题求助

Android 13 读取Download目录MP3文件权限问题解决方案

核心原因

Android 13 对存储权限做了细分:原READ_EXTERNAL_STORAGE权限被拆分为媒体类型专属权限,直接通过FileInputStream访问外部存储文件需匹配对应权限;同时未通过MediaStore索引的文件可能触发权限拦截。

解决方案步骤

1. 更新权限声明

在AndroidManifest.xml中添加适配不同版本的权限:

<!-- Android 13+ 音频媒体读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 兼容 Android 12 及以下版本 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

2. 动态申请权限

在读取文件前检查并申请对应权限:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityResultLauncher<String[]> requestPermissionLauncher =
                registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
                        result -> {
                            Boolean audioGranted = result.getOrDefault(Manifest.permission.READ_MEDIA_AUDIO, false);
                            if (audioGranted) {
                                readMp3File();
                            } else {
                                // 提示用户开启权限
                            }
                        });
        requestPermissionLauncher.launch(new String[]{Manifest.permission.READ_MEDIA_AUDIO});
    } else {
        readMp3File();
    }
} else {
    // 执行 Android 12 及以下的 READ_EXTERNAL_STORAGE 权限申请逻辑
}

3. 通过ContentResolver读取文件(替代FileInputStream)

利用MediaStore查询文件Uri,通过ContentResolver打开输入流,避免直接路径访问的权限问题:

private void readMp3File() {
    String fileName = "file.mp3";
    String relativeFolder = "Download/myFolder2/";

    Uri contentUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
    String[] projection = {MediaStore.Downloads._ID};
    String selection = MediaStore.Downloads.RELATIVE_PATH + " = ? AND " + MediaStore.Downloads.DISPLAY_NAME + " = ?";
    String[] selectionArgs = {relativeFolder, fileName};

    Cursor cursor = getContentResolver().query(contentUri, projection, selection, selectionArgs, null);
    if (cursor != null && cursor.moveToFirst()) {
        long fileId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID));
        Uri fileUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, fileId);

        try (InputStream inputStream = getContentResolver().openInputStream(fileUri)) {
            if (inputStream != null) {
                // 执行文件读取逻辑,如转为byte数组或直接处理流
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        cursor.close();
    }
}

4. 优化下载逻辑(确保文件被系统索引)

下载时通过MediaStore写入文件,自动完成系统索引,避免后续读取时的识别问题:

private void downloadMp3ToDownloadFolder(String fileName) {
    ContentValues values = new ContentValues();
    values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
    values.put(MediaStore.Downloads.RELATIVE_PATH, "Download/myFolder2");
    values.put(MediaStore.Downloads.MIME_TYPE, "audio/mpeg");
    values.put(MediaStore.Downloads.IS_PENDING, 1);

    Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
    if (uri != null) {
        try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
            // 将下载的MP3数据写入outputStream
            // 写入完成后标记文件为已就绪
            values.clear();
            values.put(MediaStore.Downloads.IS_PENDING, 0);
            getContentResolver().update(uri, values, null, null);
        } catch (IOException e) {
            e.printStackTrace();
            // 写入失败时删除无效记录
            getContentResolver().delete(uri, null, null);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:06:07