从下载目录选择文件时应用崩溃问题求助
解决Android下载文件夹文件选择时的URI查询崩溃问题
嘿,我看你在使用FileUtils获取下载文件夹文件路径时遇到了崩溃,错误是Unknown URI: content://downloads/public_downloads/230。这个问题其实挺常见的,主要是因为Downloads Provider在部分设备或Android版本下,不会给所有下载文件暴露_data列的路径,尤其是Android 10及以上的分区存储限制,直接通过ContentUris.withAppendedId构建的URI查询会失败。
问题根源
你的崩溃发生在getDataColumn方法的查询步骤,因为系统无法识别这个下载文件的URI对应的_data路径——要么是文件不在公共下载目录,要么是系统出于隐私/存储限制,没有提供这个路径的访问权限。
修复方案
我们需要修改FileUtils中处理Downloads文件的逻辑,不再依赖查询_data列,而是通过ContentResolver直接读取文件流,将文件复制到应用的私有缓存目录,返回缓存文件的路径(这是Android推荐的合规方式,尤其是新版本系统)。
步骤1:添加辅助方法到FileUtils
先加两个工具方法:一个用来获取文件名,另一个把下载文件复制到应用缓存目录:
import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.OpenableColumns; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; // 放在FileUtils类里 private static String copyFileToCache(Context context, Uri uri) { try { InputStream inputStream = context.getContentResolver().openInputStream(uri); if (inputStream == null) return null; // 获取原文件的显示名称,作为缓存文件名 String fileName = getFileName(context, uri); // 应用私有缓存目录,无需额外权限 File cacheFile = new File(context.getCacheDir(), fileName); OutputStream outputStream = new FileOutputStream(cacheFile); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } inputStream.close(); outputStream.close(); return cacheFile.getAbsolutePath(); } catch (IOException e) { if (DEBUG) Log.e(TAG, "复制文件到缓存失败", e); return null; } } private static String getFileName(Context context, Uri uri) { String fileName = "temp_" + System.currentTimeMillis(); Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (nameIndex != -1) { fileName = cursor.getString(nameIndex); } cursor.close(); } return fileName; }
步骤2:修改DownloadsDocument的处理逻辑
替换getPath方法中处理DownloadsProvider的代码块:
// DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); // 处理raw类型的URI,直接返回路径 if (id.startsWith("raw:")) { return id.replaceFirst("raw:", ""); } try { // 优先复制文件到缓存目录,返回缓存路径 return copyFileToCache(context, uri); } catch (Exception e) { // 兼容旧系统,如果复制失败,尝试原查询方式 final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); String fallbackPath = getDataColumn(context, contentUri, null, null); if (fallbackPath != null) { return fallbackPath; } // 所有方式都失败,返回null return null; } }
额外注意事项
- 权限问题:Android 6.0+需要动态申请
READ_EXTERNAL_STORAGE权限;Android 10+使用分区存储时,无需该权限即可通过ContentResolver访问下载文件。 - 推荐做法:如果你的业务只是读取文件内容(比如上传、解析),其实可以直接使用
Uri配合ContentResolver操作,不需要转换为文件路径,这样能避免很多路径相关的兼容性问题。
这样修改后,就能解决下载文件夹文件选择时的崩溃问题,同时兼容更多设备和Android版本。
内容的提问来源于stack exchange,提问作者HB.
相关产品推荐
相关产品推荐

