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

ImageIO.write缩放编辑图片后方向错误 出现90度旋转问题

问题原因
  • 手机/相机拍摄生成的JPEG图片通常携带EXIF元数据,其中Orientation字段记录了图片的正确显示方向。直接保存原始上传字节时EXIF信息完整,看图工具、浏览器会自动根据该字段校正图片朝向,所以显示正常。
  • 当前缩放逻辑通过ImageIO读取图片时仅加载传感器输出的原始像素矩阵,不会自动根据EXIF方向做旋转校正;重新编码输出图片时也没有保留、校正EXIF方向信息,最终输出的图片就会出现90度旋转的异常。
  • 现有rescale方法存在逻辑顺序错误:先执行了图片绘制、g.dispose()释放资源操作,之后才设置抗锯齿、插值算法等渲染参数,这些参数完全不会生效。
  • 额外兼容问题:代码中输出格式指定为jpg,但使用了TYPE_INT_ARGB带透明通道的图片类型,JPEG不支持透明通道,部分场景下会出现图片红底、色块异常。
修复步骤
  1. 读取上传图片的EXIF方向值,根据方向值对原始BufferedImage做对应的旋转、翻转校正,再执行缩放逻辑。
  2. 调整Graphics2D的参数设置顺序:所有渲染参数在绘制图片前配置,绘制完成后再释放资源。
  3. 输出JPEG格式时统一转成无透明通道的RGB类型,避免色块问题。
修复后代码

如果使用Maven管理依赖,先引入轻量EXIF解析依赖:

<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.19.0</version>
</dependency>

调整后的图片压缩工具代码:

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

public class CompressImages {
    // EXIF标准方向值定义
    private static final int ORIENTATION_NORMAL = 1;
    private static final int ORIENTATION_ROTATE_90 = 6;
    private static final int ORIENTATION_ROTATE_180 = 3;
    private static final int ORIENTATION_ROTATE_270 = 8;

    public static byte[] compress(byte[] img) throws IOException {
        try (InputStream inputStream = new ByteArrayInputStream(img)) {
            // 读取原始图片像素
            BufferedImage originalImage = ImageIO.read(inputStream);
            // 读取EXIF方向标记
            int orientation = getExifOrientation(img);
            // 按方向校正图片
            BufferedImage correctedImage = rotateByExifOrientation(originalImage, orientation);
            // 执行缩放
            return rescale(correctedImage).toByteArray();
        }
    }

    private static int getExifOrientation(byte[] img) {
        try (InputStream is = new ByteArrayInputStream(img)) {
            Metadata metadata = ImageMetadataReader.readMetadata(is);
            ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
            if (directory != null && directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
                return directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
            }
        } catch (Exception e) {
            // EXIF读取失败默认按正常方向处理
        }
        return ORIENTATION_NORMAL;
    }

    private static BufferedImage rotateByExifOrientation(BufferedImage image, int orientation) {
        int width = image.getWidth();
        int height = image.getHeight();
        AffineTransform transform = new AffineTransform();
        int newWidth = width;
        int newHeight = height;

        switch (orientation) {
            case ORIENTATION_ROTATE_90:
                transform.rotate(Math.PI / 2, width / 2.0, height / 2.0);
                transform.translate(0, height - width);
                newWidth = height;
                newHeight = width;
                break;
            case ORIENTATION_ROTATE_180:
                transform.rotate(Math.PI, width / 2.0, height / 2.0);
                transform.translate(-width, -height);
                break;
            case ORIENTATION_ROTATE_270:
                transform.rotate(Math.PI * 3 / 2, width / 2.0, height / 2.0);
                transform.translate(width - height, 0);
                newWidth = height;
                newHeight = width;
                break;
            default:
                return image;
        }

        // JPG不支持透明通道,统一使用RGB类型
        BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = rotatedImage.createGraphics();
        g.setTransform(transform);
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return rotatedImage;
    }

    private static ByteArrayOutputStream rescale(BufferedImage bi) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int originalWidth = bi.getWidth();
        int originalHeight = bi.getHeight();
        // 缩放为原尺寸的1/4
        int targetWidth = originalWidth / 4;
        int targetHeight = originalHeight / 4;

        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.drawImage(bi, 0, 0, targetWidth, targetHeight, null);
        g.dispose();

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

如果不想引入第三方依赖,也可以通过JDK自带的ImageIO接口解析图片元数据获取Orientation字段,只是代码更繁琐,metadata-extractor是目前EXIF兼容性最高的轻量实现,Jar包体积仅300KB左右。

内容的提问来源于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 11:06:24