Java图片旋转,某些图片默认先转正再按旋转角度旋转的问题

157 阅读3分钟

做了一个图片旋转的功能,根据旋转角度自动转正,但是发现有的原图片打开就是歪了 90 度,用方法旋转了九十度之后,还是歪 90 度,旋转 180 度就是转正后再旋转 180 度

感觉就是旋转的时候好像是先默认转正了

经过查询资料发现有一种说法是:手机拍的照片,元数据会自带旋转角度,而使用原生方法或者 hutool 的旋转方法都会先转正,再进行角度旋转

原来的旋转方法

public static byte[] rotateImage(InputStream inputStream, String outputPath, Double angle) throws IOException {
    if (angle == null || angle == 0) {
        return IoUtil.readBytes(inputStream);
    }
    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
        // 读取输入流中的图片,返回一个 BufferedImage 对象
        BufferedImage originalImage = ImageIO.read(inputStream);
        if (originalImage == null) {
            // 如果无法读取到有效的图片,抛出异常
            throw new IOException("无法从输入流中读取有效的图片");
        }

        // 获取原图片的宽度和高度
        int width = originalImage.getWidth();
        int height = originalImage.getHeight();

        // 计算旋转后的新尺寸
        double radians = Math.toRadians(angle); // 将角度转换为弧度
        double sin = Math.abs(Math.sin(radians)); // 计算正弦值
        double cos = Math.abs(Math.cos(radians)); // 计算余弦值
        // 根据旋转角度计算新的宽度和高度
        int newWidth = (int) Math.round(width * cos + height * sin);
        int newHeight = (int) Math.round(width * sin + height * cos);

        // 创建旋转变换
        AffineTransform transform = new AffineTransform();
        transform.translate(newWidth / 2.0, newHeight / 2.0); // 平移到新中心
        transform.rotate(radians);                             // 进行旋转变换
        transform.translate(-width / 2.0, -height / 2.0);     // 平移回原图中心到原点

        // 创建用于存放旋转后图片的 BufferedImage
        BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, originalImage.getType());

        // 绘制旋转后的图片
        Graphics2D g = rotatedImage.createGraphics();
        try {
            // 设置渲染参数以提升图片质量
            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.setBackground(Color.WHITE); // 设置背景颜色为白色
            g.clearRect(0, 0, newWidth, newHeight); // 清空缓冲区区域

            // 应用变换并绘制原图
            g.drawImage(originalImage, transform, null);
        } finally {
            // 释放图形上下文资源
            g.dispose();
        }

        // 确定输出格式,默认为 png 格式
        String formatName = "jpeg";
        int dotIndex = outputPath.lastIndexOf('.'); // 查找文件扩展名的位置
        if (dotIndex > 0) {
            // 提取扩展名,作为输出格式
            formatName = outputPath.substring(dotIndex + 1);
        }

        // 保存旋转后的图片到输出流
        if (!ImageIO.write(rotatedImage, formatName, outputStream)) {
            // 如果图片格式不被支持,抛出异常
            throw new IOException("不支持的文件格式: " + formatName);
        }
        return outputStream.toByteArray();
    }
}

修改后的方法

public static byte[] rotateImage(InputStream inputStream, String outputPath, Double angle) throws IOException {
        if (angle == null || angle == 0) {
            return IoUtil.readBytes(inputStream);
        }
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 将 InputStream 转换为字节数组
            byte[] imageBytes = toByteArray(inputStream);

            // 读取 Exif Orientation
            int orientation;
            try (ByteArrayInputStream exifInputStream = new ByteArrayInputStream(imageBytes)) {
                orientation = getExifOrientation(exifInputStream);

                try(ByteArrayInputStream rotateInputStream = new ByteArrayInputStream(imageBytes)) {
                    Image image ;
                    if (orientation != 1) {
                        image = ImgUtil.rotate(ImageIO.read(rotateInputStream), 0);
                    } else {
                        image = ImgUtil.rotate(ImageIO.read(rotateInputStream), (int) Math.round(angle));
                    }
                    ImgUtil.write(image, FileUtil.file(outputPath));

                    // 确定输出格式,默认为 png 格式
                    String formatName = "jpeg";
                    int dotIndex = outputPath.lastIndexOf('.'); // 查找文件扩展名的位置
                    if (dotIndex > 0) {
                        // 提取扩展名,作为输出格式
                        formatName = outputPath.substring(dotIndex + 1);
                    }

                    // 保存旋转后的图片到输出流
                    ImgUtil.write(image, formatName, outputStream);
                    return outputStream.toByteArray();
                }
            }


        } catch (Exception e) {
            log.error("图片旋转失败", e);
        }
        return null;
    }

    public static int getExifOrientation(InputStream inputStream) throws Exception {
        Metadata metadata = JpegMetadataReader.readMetadata(inputStream);
        ExifIFD0Directory exif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
        return exif != null ? exif.getInt(ExifIFD0Directory.TAG_ORIENTATION) : 1;
    }
    // 将 InputStream 转换为字节数组
    public static byte[] toByteArray(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[1024];
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }
        buffer.flush();
        return buffer.toByteArray();
    }

解决思路

getExifOrientation 方法获取是不是 1 ,如果是 1 ,说明原图片不会转正再旋转,否则的话会先转正再旋转,那么可以只旋转 0 度就可以了。