Android调用图库选择图片无法正确获取文件大小,附代码求解决方案
问题根因
- 最核心的错误不是权限,是你直接把
content://类型的Uri通过getEncodedPath()转本地File的逻辑完全错误。系统图库返回的Uri是ContentProvider提供的内容标识符,不是本地文件的真实路径,直接构造File得到的是无效对象,自然读不到长度。 - 其次你的权限申请逻辑有两处遗漏:Android 11及以上的
MANAGE_EXTERNAL_STORAGE权限不能直接通过Dexter申请,需要跳转系统设置页用户手动授权;另外Dexter的权限结果回调是空实现,你没有判断权限是否真的申请成功,就直接开放了选图功能。
修复方案
1. 修复图片大小读取逻辑
修改onActivityResult中的代码,通过ContentResolver正确查询图片大小,同时如果后续压缩需要用到本地File,需要把Uri内容拷贝到App私有目录:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == RESULT_IMAGE && data != null) { btnCompress.setVisibility(View.VISIBLE); final Uri imageUri = data.getData(); if (imageUri == null) { Toast.makeText(this, "无效的图片", Toast.LENGTH_SHORT).show(); return; } try { // 原有展示图片逻辑保留 final InputStream imageStream = getContentResolver().openInputStream(imageUri); final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream); imgOriginal.setImageBitmap(selectedImage); // 正确获取图片大小逻辑 long fileSize = 0; // 优先通过MediaStore查询大小 Cursor cursor = getContentResolver().query(imageUri, new String[]{MediaStore.Images.Media.SIZE}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int sizeIndex = cursor.getColumnIndex(MediaStore.Images.Media.SIZE); if (sizeIndex >= 0) { fileSize = cursor.getLong(sizeIndex); } cursor.close(); } // 查询失败则通过InputStream长度获取 if (fileSize <= 0 && imageStream != null) { fileSize = imageStream.available(); imageStream.close(); } txtOriginal.setText("Size: " + Formatter.formatShortFileSize(this, fileSize)); Toast.makeText(MainActivity.this, "Size: " + Formatter.formatShortFileSize(this, fileSize), Toast.LENGTH_LONG).show(); // 压缩需要用到本地File的话,先把Uri内容拷贝到私有目录 originalImage = copyUriToTempFile(imageUri); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "读取图片失败", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "未选择图片", Toast.LENGTH_SHORT).show(); } } // Uri转本地临时File工具方法 private File copyUriToTempFile(Uri uri) throws IOException { File tempFile = new File(getExternalCacheDir(), "temp_origin_img.jpg"); InputStream inputStream = getContentResolver().openInputStream(uri); FileOutputStream outputStream = new FileOutputStream(tempFile); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } inputStream.close(); outputStream.close(); return tempFile; }
2. 权限逻辑修复
Dexter使用调整
Android 11(API 30)以上的MANAGE_EXTERNAL_STORAGE属于特殊权限,Dexter无法直接申请,需要单独处理,同时要在回调中判断权限申请结果:
private void askPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 先判断是否已获得存储管理权限 if (!Environment.isExternalStorageManager()) { // 跳转系统设置页手动授权 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1001); return; } } // 低于30版本或已获得管理权限的,申请基础存储权限 Dexter.withContext(this) .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { if (!report.areAllPermissionsGranted()) { Toast.makeText(MainActivity.this, "存储权限被拒绝,无法使用选图功能", Toast.LENGTH_SHORT).show(); btnPick.setEnabled(false); } else { btnPick.setEnabled(true); } } @Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> list, PermissionToken token) { token.continuePermissionRequest(); } }).check(); }
注意:如果你的压缩文件都存储在App私有目录(
getExternalFilesDir()、getCacheDir()这类路径),Android 10及以上不需要申请WRITE_EXTERNAL_STORAGE权限,Android 13及以上只需要申请READ_MEDIA_IMAGES权限即可,无需申请存储管理权限。
替代权限方案
如果不想引入Dexter框架,可以直接用官方推荐的Activity Result API实现权限申请,无需依赖第三方库。
内容的提问来源于stack exchange,提问作者flm
相关产品推荐
相关产品推荐

