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

Android 10下如何将Documents文件夹中的文件复制到应用缓存文件夹?文件存在却返回‘No file found’日志问题求助

在Android 10中复制Documents文件夹中非应用创建文件的问题解决方案

问题描述

我正尝试在Android 10中将Documents文件夹中的文件复制到缓存文件夹。由于Android 10必须使用MediaStore,我编写了相关代码,但实际文件存在的情况下,日志却返回‘No file found’。想请教如何复制或读取非本应用创建的文件?

相关代码如下:

File deviceDB = new File(getDatabasePath(Define.DBNAME).toString() + ".db");
Uri contentUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/" + Define.BACKUP_DIR + "/"};
Cursor cursor = getContentResolver().query(contentUri, null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() == 0) {
    Log.i("debug","No file found");
} else {
    while (cursor.moveToNext()) {
        String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
        if (fileName.equals(dbName + ".db")) {
            long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
            uri = ContentUris.withAppendedId(contentUri, id);
            break;
        }
    }
    if (uri == null) {
        Log.i("debug","uri == null");
    } else {
        InputStream inputStream = getContentResolver().openInputStream(uri);
        int size = inputStream.available();
        byte[] bytes = new byte[size];
        inputStream.read(bytes);
        OutputStream output = new FileOutputStream(deviceDB);
        output.write(bytes);
        output.flush();
        inputStream.close();
    }
}

问题分析与解决方案

1. 先搞定权限问题

Android 10里,哪怕用MediaStore读取非本应用创建的文件,也得有READ_EXTERNAL_STORAGE权限。首先在Manifest里声明:

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

别忘了还要在运行时动态申请这个权限,等用户授权后再去访问文件,不然系统会直接拦截你的请求。

2. 修正RELATIVE_PATH的匹配逻辑

你代码里RELATIVE_PATH的条件带了末尾斜杠/,但MediaStore里存储的路径大概率是不带末尾斜杠的(比如实际存的是Documents/YourBackupDir而非Documents/YourBackupDir/)。你可以试试去掉末尾的斜杠:

String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/" + Define.BACKUP_DIR};

怕有兼容问题的话,也可以用模糊匹配:

String selection = MediaStore.MediaColumns.RELATIVE_PATH + " LIKE ?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/" + Define.BACKUP_DIR + "%"};

3. 明确查询的列,避免数据缺失

你现在查询时没指定具体列,可能导致获取的信息不全。建议明确指定需要的列,既高效又能确保拿到关键数据:

String[] projection = {
    MediaStore.MediaColumns._ID,
    MediaStore.MediaColumns.DISPLAY_NAME,
    MediaStore.MediaColumns.RELATIVE_PATH
};
Cursor cursor = getContentResolver().query(contentUri, projection, selection, selectionArgs, null);

4. 优化文件读取的健壮性

原代码里用inputStream.available()获取文件大小很不靠谱,这个方法返回的是当前瞬间能读取的字节数,不是文件总大小。换成循环读取的方式更稳妥,还能避免大文件读取不完整:

if (uri != null) {
    try (InputStream inputStream = getContentResolver().openInputStream(uri);
         OutputStream output = new FileOutputStream(deviceDB)) {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        output.flush();
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("debug", "文件复制失败: " + e.getMessage());
    }
}

这里用了try-with-resources语法,能自动关闭流,不用手动写close,减少资源泄漏的风险。

5. 试试更合规的SAF方案

Android 10里,对于非媒体文件(比如你的.db文件),除了MediaStore,还可以用**存储访问框架(SAF)**让用户手动选择文件。这种方式不需要申请存储权限,更符合Android的隐私规范。比如通过ACTION_OPEN_DOCUMENT启动文件选择器,拿到用户选中的文件Uri后再复制:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*"); // 可以指定具体类型,比如application/vnd.sqlite3
startActivityForResult(intent, PICK_FILE_REQUEST_CODE);

然后在onActivityResult里获取Uri,再执行复制操作即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:12:35