IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> JAVA实现图片质量压缩和加水印 -> 正文阅读

[移动开发]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.测试接口

请求接口测试
日志输出
添加完水印图片展示


总结

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

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

解决办法:

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

配置文件展示

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:22:54  更:2022-04-04 12:25:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:23:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码