JAVA实现图片质量压缩和加水印

这个世界没有什么好畏惧的,反正我们只来一次。

文章目录

前言

主要实现了两个功能:

  • 加水印
  • 质量压缩

编写代码

1.编写工具类

ImageUtil代码如下:

package com.example.demo.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

/** * 图片相关的工具类 * @author GMaya * @dateTime 2022/3/29 14:21 */
@Slf4j
public class ImageUtil { 
    /** * 最小压缩质量参数 */
    private static final float QUALITY    = 0.09f;
    /** * 指定压缩大小,单位为b字节 1kb = 1024b */
    private static final float LIMIT_SIZE = 500 * 1024;

    /** * 质量压缩图片,循环压缩到指定大小 * @param bytes 原图片byte数组 * @return 压缩后图片byte数组 */
    public static byte[] compressPic(byte[] bytes) { 
        log.info("压缩前大小:{}{}", bytes.length / 1024, "kb");
        // 如果图片小于指定压缩大小,则直接返回。
        if (bytes.length < LIMIT_SIZE) { 
            return bytes;
        }
        TimeInterval timer = DateUtil.timer();
        BufferedImage bufferedImage = null;
        // 初始化质量参数
        float q = 0.9f;
        byte[] result = null;
        try { 
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            bufferedImage = ImageIO.read(in);
            // 最小压缩到0.1质量,不管有没有到500kb
            while (q > QUALITY) { 
                result = compressPic(bufferedImage, q);
                log.info("图片压缩后大小:{}{},质量参数:{}", result.length / 1024, "kb", q);
                if (result.length > LIMIT_SIZE) { 
                    // 默认压缩到500kb,如果大于500kb,继续压缩
                    q -= 0.1f;
                } else { 
                    q = 0f;
                }
            }
            //花费毫秒数
            long interval = timer.interval();
            log.info("图片压缩耗时:{}{}", interval, "ms");
            return result;
        } catch (IOException e) { 
            e.printStackTrace();
        }
        return null;
    }

    /** * 图片加水印 * @param pressText 水印文字 * @param targetImg 要加水印的图片路径 */
    public static void pressText(String pressText, String targetImg) { 
        try { 
            TimeInterval timer = DateUtil.timer();

            BufferedImage src = ImageIO.read(new File(targetImg));
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            // 动态设置字体大小 , 以300像素15的基数大小设置
            int fontSize = width < height ? width / 300 * 15 : height / 300 * 15;
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            // 设置水印字体颜色. TODO 也可以变为动态参数传进来.
            g.setColor(Color.RED);
            // 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体. TODO 也可以变为动态参数传进来.
            g.setFont(new Font("宋体", Font.PLAIN, fontSize));
            // 透明度
// g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));
            /* 消除java.awt.Font字体的锯齿 */
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            /** * TODO 如果是多行水印,以及超长水印,需要特殊处理.后期可升级. * 思路:将水印分隔成数组pressTextList,循环数组 * int y = 1 * for(String text : pressTextList){ * g.drawString(text, 20, y * fontSize * 12 / 10); * } */
            // 开始加水印
            g.drawString(pressText, 20, fontSize * 12 / 10);
            // 输出图像
            g.dispose();

            File f = new File(targetImg);
            ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);

            //花费毫秒数
            long interval = timer.interval();
            log.info("图片加水印耗时:{}{}", interval, "ms");
        } catch (Exception e) { 
            e.printStackTrace();
        }
    }

    /** * * @param image * @param quality 参数qality是取值0~1范围内,压缩程度 * @return * @throws IOException */
    public static byte[] compressPic(BufferedImage image, float quality) throws IOException { 
        ByteArrayOutputStream outArray = new ByteArrayOutputStream();
        // 指定写图片的方式为 jpg
        ImageWriter imgWrier = ImageIO.getImageWritersByFormatName("jpg").next();
        ImageWriteParam imgWriteParams = new javax.imageio.plugins.jpeg.JPEGImageWriteParam(null);
        // 要使用压缩,必须指定压缩方式为MODE_EXPLICIT
        imgWriteParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        // 设置压缩质量参数,参数qality是取值0~1范围内,
        imgWriteParams.setCompressionQuality(quality);
        imgWriteParams.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
        ColorModel colorModel = image.getColorModel();
        // 指定压缩时使用的色彩模式
        imgWriteParams.setDestinationType(
            new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
        try { 
            imgWrier.reset();
            // 必须先指定 out值,才能调用write方法, ImageOutputStream可以通过任何
            // OutputStream构造
            imgWrier.setOutput(ImageIO.createImageOutputStream(outArray));
            // 调用write方法,就可以向输入流写图片
            imgWrier.write(null, new IIOImage(image, null, null), imgWriteParams);
        } catch (Exception e) { 
            e.printStackTrace();
        } finally { 
            imgWrier.dispose();
        }
        return outArray.toByteArray();
    }

}

2.编写接口

ImageController代码如下:

package com.example.demo.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.demo.util.ImageUtil;
import com.example.demo.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

/** * 用于图片处理的接口 * @author GMaya * @dateTime 2022/3/29 14:13 */
@Slf4j
@RestController
@RequestMapping("/image")
public class ImageController { 

    /** * 压缩图片/图片加水印 * @param file 原图片文件 * @param jsonObject 传递的其他参数 * @return * @throws IOException */
    @PostMapping("/compressPic")
    public R compressPic(MultipartFile file, String jsonObject) throws IOException { 
        if (file == null || file.isEmpty()) { 
            return R.error("文件不能为空,请检查!");
        }
        // 计时开始
        TimeInterval timer = DateUtil.timer();
        // json 转换
        JSONObject json = JSONUtil.parseObj(jsonObject);
        // 先将文件保存到本地
        String targetImg = "D:\\work\\test-gmaya\\image\\" + IdUtil.simpleUUID() + ".jpg";
        File destImageFile = new File(targetImg);
        FileUtil.writeBytes(file.getBytes(), destImageFile);


        // 是否需要加水印,默认为0是,1否
        String isPressText = json.getStr("isPressText");
        if ("0".equals(isPressText)) { 
            String pressText = json.getStr("pressText");
            if(StrUtil.isEmpty(pressText)){ 
                return R.error("水印文字不能为空,请检查!");
            }
            ImageUtil.pressText(pressText, targetImg);
        }

        // 是否需要压缩,默认为0是,1否
        String isCompress = json.getStr("isCompress");
        if ("0".equals(isCompress)) { 
            // 压缩并保存
            byte[] bytes = ImageUtil.compressPic(file.getBytes());
            FileUtil.writeBytes(bytes, destImageFile);
        }

        //花费毫秒数
        long interval = timer.interval();
        log.info("图片处理总耗时:{}{}", interval, "ms");
        return R.data(targetImg);
    }

}

3.测试接口

《JAVA实现图片质量压缩和加水印》
《JAVA实现图片质量压缩和加水印》
《JAVA实现图片质量压缩和加水印》

总结

问题:
选择图片过大,导致失败

The field file exceeds its maximum permitted size of 1048576 bytes.

解决办法:

spring:
  servlet:
    multipart:
      max-file-size: 10MB # 单个上传文件大小限制10MB以下
      max-request-size: 100MB #总上传文件大小限制100MB以下

《JAVA实现图片质量压缩和加水印》

    原文作者:GMaya
    原文地址: https://blog.csdn.net/gfl1427097103/article/details/123893763
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞