概述
在 Android 开发中,使用 ContentResolver 通过 URI 获取文件路径时,可能会遇到以下错误:
plaintext
java.lang.IllegalArgumentException: column '_data' does not exist
主要原因是从 API 29 开始,MediaStore.Files.FileColumns.DATA 字段已经被废弃。因此,直接通过 _data 列获取文件路径的方法不再适用。
原因分析
从 Android 10(API 级别 29)开始,Google 强制执行了更严格的存储权限策略,MediaStore 中的 _data 列被废弃,取而代之的是推荐使用 FileDescriptor 来读取文件内容。
旧版代码
之前我们使用的通过 URI 获取文件路径的代码如下:
java
private void getContentResolverInfo(Uri uri, int width, int height, SlideFactory.MediaType mediaType) {
    Cursor cursor = null;
    long start = System.currentTimeMillis();
    try {
        cursor = context.getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
            long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
            long filePath = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
            String mimeType = context.getContentResolver().getType(uri);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null)
            cursor.close();
    }
}
修改后的代码
为了解决上述问题,修改后的代码如下:
java
private void getContentResolverInfo(Uri uri, int width, int height, SlideFactory.MediaType mediaType) {
    Cursor cursor = null;
    long start = System.currentTimeMillis();
    try {
        cursor = context.getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
            long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
            // 使用 FileDescriptor 读取文件内容
            ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
            if (parcelFileDescriptor != null) {
                FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                String keystoreContent = readFileContent(fileDescriptor);
                FileInputStream fileInputStream = new FileInputStream(fileDescriptor);
                String mimeType = context.getContentResolver().getType(uri);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null)
            cursor.close();
    }
}
public static String readFileContent(FileDescriptor fileDescriptor) {
    Log.i(TAG, "readFileContent(), fileDescriptor=" + fileDescriptor);
    if (fileDescriptor == null) {
        return null;
    }
    StringBuilder sb = new StringBuilder();
    try (FileInputStream fileInputStream = new FileInputStream(fileDescriptor);
         InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
         BufferedReader reader = new BufferedReader(inputStreamReader)) {
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
    return sb.toString();
}
关键改进点
- 移除对 
_data列的依赖:直接读取文件内容而不依赖_data列。 - 使用 
ParcelFileDescriptor和FileDescriptor:通过FileDescriptor读取文件内容,确保兼容新版本的 Android。 - 资源管理:使用 try-with-resources 语法确保流正确关闭,避免资源泄漏。