go to index

Android提示ContentResolver:column ‘_data‘ does not exist.错误的解决方法

read time 3 min read
Android URI 文件

概述

在 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();
}

关键改进点

  1. 移除对 _data 列的依赖:直接读取文件内容而不依赖 _data 列。
  2. 使用 ParcelFileDescriptorFileDescriptor:通过 FileDescriptor 读取文件内容,确保兼容新版本的 Android。
  3. 资源管理:使用 try-with-resources 语法确保流正确关闭,避免资源泄漏。