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

Java根据EXIF方向旋转BufferedImage后图片变黑问题

BufferedImage旋转后输出全黑问题修复

核心问题原因

你的代码存在4个直接导致输出全黑/异常的错误:

  • Graphics2D.rotate() 方法接收的参数是弧度值而非角度值,直接传入90/180/270会导致旋转角度完全错误,图片被绘制到画布可视区域外
  • 旋转时未做坐标平移:默认旋转是绕画布左上角(0,0)点执行,90°、270°旋转后如果不追加对应平移,图片会整体偏移出画布范围
  • 图形上下文操作顺序错误:你在调用g.drawImage()之后才执行g.dispose(),后续设置的渲染参数、合成规则完全不会生效,且dispose()调用后图形上下文已经释放,后续操作属于无效调用
  • 额外隐患:JPG格式不支持透明通道,使用TYPE_INT_ARGB类型写出JPG时容易出现色彩异常,且你代码注释标注缩放50%,实际宽高除以4是缩放到原尺寸的1/4,逻辑不一致。

修复后可直接运行的代码

import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.imaging.ImageMetadataReader;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;

public class ImageCompressUtil {
    public static byte[] compress(byte[] img) throws IOException {
        ByteArrayOutputStream rescale = rescale(img);
        return rescale.toByteArray();
    }

    private static Integer getOrientation(InputStream inputstream) throws Exception {
        Metadata metadata = ImageMetadataReader.readMetadata(inputstream);
        ExifIFD0Directory exifIFD0 = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
        // 无EXIF方向信息直接返回正常方向
        if (exifIFD0 == null || !exifIFD0.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
            return 1;
        }
        return exifIFD0.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    }

    private static ByteArrayOutputStream rescale(byte[] img) throws IOException {
        InputStream inputstreamMeta = new ByteArrayInputStream(img);
        InputStream inputstream = new ByteArrayInputStream(img);

        int orientation = 1;
        try {
            orientation = getOrientation(inputstreamMeta);
        } catch (Exception e) {
            // 读取EXIF失败默认按正常方向处理
        }
        BufferedImage bi = ImageIO.read(inputstream);
        int srcWidth = bi.getWidth();
        int srcHeight = bi.getHeight();

        // 计算旋转后的目标宽高,这里按缩放到原尺寸1/4设置,需要50%就把/4改成/2
        int targetWidth, targetHeight;
        AffineTransform transform = new AffineTransform();
        switch (orientation) {
            case 6: // 顺时针转90度
                targetWidth = srcHeight /4;
                targetHeight = srcWidth /4;
                // 角度转弧度,绕画布中心旋转后平移到可视区域
                transform.rotate(Math.toRadians(90), targetWidth/2d, targetHeight/2d);
                transform.translate((targetWidth - targetHeight)/2d, (targetHeight - targetWidth)/2d);
                break;
            case 3: // 转180度
                targetWidth = srcWidth /4;
                targetHeight = srcHeight /4;
                transform.rotate(Math.toRadians(180), targetWidth/2d, targetHeight/2d);
                break;
            case 8: // 顺时针转270度
                targetWidth = srcHeight /4;
                targetHeight = srcWidth /4;
                transform.rotate(Math.toRadians(270), targetWidth/2d, targetHeight/2d);
                transform.translate((targetWidth - targetHeight)/2d, (targetHeight - targetWidth)/2d);
                break;
            default: // 正常方向
                targetWidth = srcWidth /4;
                targetHeight = srcHeight /4;
                break;
        }

        // JPG不支持透明,统一用TYPE_INT_RGB避免黑块
        BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = resizedImage.createGraphics();
        // 所有渲染参数在绘图前设置
        g.setComposite(AlphaComposite.Src);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        g.setTransform(transform);
        // 绘图
        g.drawImage(bi, 0, 0, targetWidth, targetHeight, null);
        // 所有操作完成后再释放上下文
        g.dispose();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(resizedImage, "jpg",baos);
        return baos;
    }
}

关键修改点说明

  • 用Math.toRadians()把角度转为弧度值传入旋转方法,保证旋转角度正确
  • 旋转时指定绕画布中心旋转,追加对应平移量,保证旋转后的图片完整落在画布可视区域内
  • 调整Graphics2D操作顺序:先设置渲染参数、应用旋转变换,再绘制图片,最后调用dispose释放资源
  • 针对JPG格式统一使用TYPE_INT_RGB类型,避免透明通道导致的黑块问题
  • 补全EXIF目录非空判断,避免无EXIF信息的图片触发空指针异常

内容的提问来源于stack exchange,提问作者mobiw

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:51:27