ContentResolver返回文件大小异常求助(Android Oreo模拟器)
这个问题我之前也碰到过,本质是Android 8.0(API 26)开始系统相机对MediaStore字段的写入逻辑变了,具体原因和解决方案如下:
问题原因
- MediaStore.SIZE字段的统计逻辑变更:在API 25及以前,MediaStore的
MediaColumns.SIZE存储的是文件在磁盘上的实际总大小;但从API 26开始,系统相机应用在将拍摄的图片插入MediaStore时,填入的SIZE值仅对应图片的像素主体数据大小(比如JPEG文件中去除了EXIF元数据、内置缩略图APP段等额外内容后的大小),而非整个文件的实际字节数。 - adb和文件详情的统计方式:
adb ls -al和文件详情显示的是文件在存储系统中的完整大小,包含了所有附加数据(EXIF信息、JPEG缩略图块等),所以数值更大。 - Document API的一致性:系统提供的DocumentProvider对于媒体文件,直接复用了MediaStore中的
SIZE字段值,所以返回的大小和ContentResolver查询结果一致。
解决方案
不要依赖MediaColumns.SIZE字段获取准确的文件大小,而是通过ContentResolver.openFileDescriptor()获取文件的实际统计大小,这个方法会直接读取文件系统的真实数据:
private fun getActualFileSize(contentResolver: ContentResolver, fileUri: Uri): Long { return try { contentResolver.openFileDescriptor(fileUri, "r")?.use { fileDescriptor -> fileDescriptor.statSize } ?: 0L } catch (e: IOException) { // 处理异常,比如权限问题或文件不存在 0L } }
这个方法获取的statSize是文件在磁盘上的真实字节数,和adb ls显示的结果一致。
如果你需要同时获取文件名和大小,可以结合原来的查询逻辑和这个方法:
private fun getFileInfo(contentResolver: ContentResolver, fileUri: Uri): Pair<String, Long>? { val projection = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME) val metaCursor = contentResolver.query(fileUri, projection, null, null, null) metaCursor?.use { if (it.moveToFirst()) { val displayName = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)) val actualSize = getActualFileSize(contentResolver, fileUri) return Pair(displayName, actualSize) } } return null }
额外验证
你可以用exiftool查看这张图片的结构,会发现:
- 图片的JPEG主体数据大小约为29kb
- 加上EXIF元数据、内置缩略图等附加内容后,总大小正好是44/45kb,这和你看到的数值完全对应。
内容的提问来源于stack exchange,提问作者guness
相关产品推荐
相关产品推荐

