概述
在 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 语法确保流正确关闭,避免资源泄漏。