You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 16:57:03