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

Android图片压缩应用调用compressToFile空指针异常问题求助

问题排查及解决方案

错误根因

  1. Compressor库的compressToFile()方法要求传入原图片对应的File对象,你当前传入的imageBitmap是Bitmap类型,且没有对该变量做赋值操作,属于空对象
  2. 你注释掉了originalImage对象的实例化代码,后续调用originalImage.getName()也会触发空指针
  3. 即使你恢复注释代码,直接用Uri的路径创建File对象在Android 10+分区存储规则下也无效,会得到空文件
  4. onActivityResult的判断逻辑错误,把requestCode和resultCode混淆,无法正确区分相机/相册返回的结果

修复方案

第一步:声明全局变量

在Activity顶部添加以下全局变量:

private Uri originalImageUri;
private File originalImageFile;
private String originalFileName;

第二步:修改onActivityResult逻辑

修正判断逻辑,把Uri对应的文件复制到应用私有目录拿到可用的File对象:

@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);
        originalImageUri = data.getData();
        try {
            // 展示原图到控件
            final InputStream imageStream = getContentResolver().openInputStream(originalImageUri);
            final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
            imgOriginal.setImageBitmap(selectedImage);
            
            // 查询原文件的文件名
            Cursor cursor = getContentResolver().query(originalImageUri, null, null, null, null);
            int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
            cursor.moveToFirst();
            originalFileName = cursor.getString(nameIndex);
            cursor.close();
            
            // 展示原文件大小
            long size = DocumentFile.fromSingleUri(this, originalImageUri).length();
            txtOriginal.setText("Size: " + Formatter.formatShortFileSize(this, size));
            
            // 将Uri对应的文件复制到应用私有缓存目录,生成可正常读写的File对象
            originalImageFile = new File(getCacheDir(), originalFileName);
            FileOutputStream out = new FileOutputStream(originalImageFile);
            selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.flush();
            out.close();

        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "加载图片失败", Toast.LENGTH_SHORT).show();
        }

    } else {
        Toast.makeText(this, "未选择图片", Toast.LENGTH_SHORT).show();
    }
}

第三步:修改压缩按钮点击逻辑

btnCompress.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // 增加非空校验避免空指针
        if (originalImageFile == null || !originalImageFile.exists()) {
            Toast.makeText(MainActivity.this, "原文件不存在,请重新选择图片", Toast.LENGTH_SHORT).show();
            return;
        }
        String widthStr = txtWidth.getText().toString();
        String heightStr = txtHeight.getText().toString();
        if (widthStr.isEmpty() || heightStr.isEmpty()) {
            Toast.makeText(MainActivity.this, "请填写宽高参数", Toast.LENGTH_SHORT).show();
            return;
        }
        int quality = seekBar.getProgress();
        int width = Integer.parseInt(widthStr);
        int height = Integer.parseInt(heightStr);
        try {
            // 传入正确的原文件File对象
            File compressedImage = new Compressor(MainActivity.this)
                    .setMaxWidth(width)
                    .setMaxHeight(height)
                    .setQuality(quality)
                    .setCompressFormat(Bitmap.CompressFormat.JPEG)
                    .setDestinationDirectoryPath(filepath)
                    .compressToFile(originalImageFile);

            // 展示压缩后的图片和大小
            Bitmap finalBitmap = BitmapFactory.decodeFile(compressedImage.getAbsolutePath());
            imgCompressed.setImageBitmap(finalBitmap);
            txtCompressed.setText("Size: " + Formatter.formatShortFileSize(MainActivity.this, compressedImage.length()));

            // 存储逻辑说明:
            // 1. 保存为新文件:直接使用compressedImage的路径即可
            // 2. 覆盖原文件:通过ContentResolver将compressedImage写入originalImageUri对应的系统媒体库路径即可
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this, "压缩失败", Toast.LENGTH_SHORT).show();
        }
    }
});

注意事项

  • 提前申请对应存储权限:Android 13+需要申请READ_MEDIA_IMAGES权限,Android 10及以下需要申请WRITE_EXTERNAL_STORAGE权限
  • 覆盖原文件时不能直接修改系统媒体库路径的文件,需要通过ContentResolver操作
  • 如果需要保留EXIF信息,复制原文件到缓存目录时建议直接复制IO流而不是压缩Bitmap

内容的提问来源于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 17:45:03