Java 保质压缩图片

Java 保质压缩图片

一、需求背景

📅 2025.03.25 最近接到一个图片保存的需求。心想这不就是调用 API,上传到云存储平台上去吗。难点不过是云存储平台 API的配置和使用。

但现实狠狠给了我一巴掌,实际情况是:需要将图片保存到数据库中去

项目组长给了我一个提示:图片转为 Base64编码,既可以后端存到数据库中去,也可以前端取出数据,直接显示 Base64编码的图片。

嗯,不错,Hutool也有现成的 Base64工具类。实际操作后发现,图片一大(1M)以上,数据库就超级慢,但现在手机图片都动辄 3M,那数据库不早晚得崩溃。

数据库慢的原因有以下两个:

  1. Base64编码后,数据会增大 30%;
  2. 图片太大。

因此,使用 Base64编码还不是最优解,以及图片还需要保质压缩(图片体积变小,但是画质要好,不能压缩后啥也看不到了)。

通过百度和 DeepSeek,了解到数据库可以存放二进制数据,然后只需要编写图片压缩代码并转为 byte[]即可。


二、功能实现

环境:SpringBoot2.7Java8

引入的依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.36</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-imaging</artifactId>
    <version>1.0.0-alpha5</version>
</dependency>

具体代码:

package com.rnny.myapi.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.imaging.Imaging;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Date;

@Slf4j
public class ImageUtility {


    /**
     * 根据图片网络地址来下载图片,保存到默认的本地磁盘,并返回图片的byte[]
     *
     * @param imageSourceUrl 图片的网络地址
     * @return 图片 base64 编码数据,不存在或报错则返回byte[0]
     */
    public static byte[] downloadImageUseHutool(String imageSourceUrl) {
        return downloadImageUseHutool(imageSourceUrl, null);
    }

    /**
     * 根据图片网络地址来下载图片,保存到指定的本地磁盘,并返回图片的byte[]
     *
     * @param imageSourceUrl 图片的网络地址
     * @param localDiskPath  图片的本地磁盘路径
     * @return 图片 base64 编码数据,不存在或报错则返回byte[0]
     */
    public static byte[] downloadImageUseHutool(String imageSourceUrl, String localDiskPath) {
        if (StrUtil.isEmpty(imageSourceUrl)) {
            return null;
        }
        if (StrUtil.isEmpty(localDiskPath)) {
            localDiskPath = "D:/images/" + DateUtil.formatDate(new Date()) + "/" + System.currentTimeMillis() + ".jpg";
        }
        // try-catch捕获图片下载地址无效异常
        try {
            // 1、下载网络图片,保存到本地并返回图片file
            File file = HttpUtil.downloadFileFromUrl(imageSourceUrl, localDiskPath);
            return resizeImage(file);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            // 路径日志,正式环境删除即可
            log.info("图片网络地址:{}", imageSourceUrl);
            log.info("图片的本地磁盘路径:{}", localDiskPath);
        }
        return new byte[0];
    }

    /**
     * 自动压缩图片,并返回图片的byte[]
     *
     * @param inputImageFile 图片文件
     * @return 图片压缩后的byte[],如果不存在或报错则返回byte[0]
     */
    public static byte[] resizeImage(File inputImageFile) {
        try {
            // 1. 读取原始图片
            BufferedImage originalImage = Imaging.getBufferedImage(inputImageFile);
            // 2. 计算等比缩放后的高度(压缩图片)
            double ratio = 0.9;
            if (inputImageFile.length() > 4194304) {
                // 大于4M
                ratio = 0.5;
            } else if (inputImageFile.length() > 3145728) {
                // 大于3M
                ratio = 0.6;
            } else if (inputImageFile.length() > 1048576) {
                // 大于2M
                ratio = 0.7;
            } else if (inputImageFile.length() > 102400) {
                // 大于100K
                ratio = 0.8;
            }
            int targetWidth = (int) (originalImage.getWidth() * ratio);
            int targetHeight = (int) (originalImage.getHeight() * ratio);

            // 3. 创建目标尺寸的RGB图像(去除alpha通道)
            BufferedImage resizedImage = new BufferedImage(
                    targetWidth,
                    targetHeight,
                    BufferedImage.TYPE_INT_RGB);

            // 4. 高质量缩放绘制
            Graphics2D g = resizedImage.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            g.drawImage(originalImage,
                    0, 0, targetWidth, targetHeight,
                    0, 0, originalImage.getWidth(), originalImage.getHeight(),
                    null);
            g.dispose();

            // 5. 输出为JPEG字节数组
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                ImageIO.write(resizedImage, "jpg", baos);
                return baos.toByteArray();
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return new byte[0];
    }

    // 测试功能是否可以使用,正式环境删除即可。
    public static void main(String[] args) {
        String imageSourceUrl = "https://pic.rmb.bdstatic.com/bjh/618296445392de1b74e60c5065d5edf0.jpeg@h_1280";
        ImageUtility.downloadImageUseHutool(imageSourceUrl);
    }
}


三、参考文章

  1. DeepSeek.
posted @ 2025-03-25 15:22  软柠柠吖  阅读(104)  评论(0)    收藏  举报