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

Android 13无法读取Download目录下手动复制的文本文件问题求助

Android 13下无法读取Download目录手动复制的文本文件

我刚把平板升级到Android 13,现在应用打不开手动复制到/Download目录下的文本文件。之前应用已经申请了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,但就算权限已授予,还是会抛出IOException: open failed: EACCES (Permission denied)。

我的应用需求很明确:打开用户复制到/Download目录下的指定文本文件(route.txt),把内容读取到String变量里。谷歌是不是限制了这个已经允许十多年的操作?要通知两万多名用户更改业务逻辑简直是天大的难题。

已尝试的操作

我已经检查并请求了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限(未授权时会发起请求),获取权限后尝试读取文件内容:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

但就算权限已授予,打开文件时还是出现EACCES错误,读取文件的代码如下:

static String readRouteFile(String sFile){
    Log.d(TAG, "readRouteFile(" + sFile + ")");

    File f = new File(sFile);

    if(f.exists()){
        Log.d(TAG, "readRouteFile.exists");

        StringBuilder sb = new StringBuilder();
        FileInputStream fis;
        String sbuf;
        try {
            fis = new FileInputStream(f);
            BufferedReader bReader = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8));
            while ((sbuf = bReader.readLine()) != null ) {
                sb.append(sbuf).append('\n');
            }

            bReader.close();
            fis.close();

            Log.d(TAG, "readRouteFile.size:" + sb.length());
            return sb.toString();
        }catch(IOException e){
            Log.e(TAG, "readRouteFile.IOException:" + e.getMessage());
        }

    }else{
        Log.e(TAG, "readRouteFile.!exist:" + sFile);
    }

    return null;
}//readRouteFile

问题原因与解决方案

Android 13对外部存储权限做了细分限制,原有的READ_EXTERNAL_STORAGE不再能访问Download等公共目录下的非媒体文件(比如你的txt文件)。针对你的需求,有两种可行方案:

方案1:用MediaStore API读取文件

Android 13推荐通过MediaStore访问公共目录下的文件,无需额外权限。对于文本文件,可通过MediaStore查询获取文件Uri,再通过ContentResolver读取:

public static String readRouteFileFromMediaStore(Context context) {
    String[] projection = {MediaStore.Files.FileColumns.DISPLAY_NAME};
    String selection = MediaStore.Files.FileColumns.DISPLAY_NAME + " = ?";
    String[] selectionArgs = {"route.txt"};

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

    if (cursor != null && cursor.moveToFirst()) {
        int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
        long id = cursor.getLong(idColumn);
        Uri uri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id);
        cursor.close();

        try (InputStream is = context.getContentResolver().openInputStream(uri)) {
            if (is == null) return null;
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append('\n');
            }
            return sb.toString();
        } catch (IOException e) {
            Log.e(TAG, "readRouteFile.IOException:" + e.getMessage());
        }
    }
    if (cursor != null) cursor.close();
    return null;
}

方案2:让用户手动选择文件(ACTION_OPEN_DOCUMENT)

如果用户只需要读取特定的route.txt,可以让用户通过系统文件选择器手动选择,获取Uri后持久化权限,后续无需重复选择:

// 定义请求码
private static final int REQUEST_CODE_OPEN_DOC = 1001;

// 启动文件选择器
private void openRouteFilePicker() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("text/plain");
    startActivityForResult(intent, REQUEST_CODE_OPEN_DOC);
}

// 处理选择结果
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_OPEN_DOC && resultCode == RESULT_OK && data != null) {
        Uri uri = data.getData();
        // 持久化读取权限
        getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        // 读取文件内容
        try (InputStream is = getContentResolver().openInputStream(uri)) {
            if (is == null) return;
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append('\n');
            }
            String fileContent = sb.toString();
            // 处理读取到的内容
        } catch (IOException e) {
            Log.e(TAG, "readRouteFile.IOException:" + e.getMessage());
        }
    }
}

权限失效的核心原因

Android 13开始,READ_EXTERNAL_STORAGE仅能访问媒体文件(图片、视频、音频),无法访问Download目录下的非媒体文件。谷歌这么做是为了强化隐私保护,减少应用对用户文件的无差别访问权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:13:16