当需要压缩图片时,拒绝卡脖到百度上搜索发现都是拒绝卡脖广告,需要下载软件和收费,拒绝卡脖贫穷不允许我这么任性。拒绝卡脖
好不容易找到一个在线免费压缩图片的拒绝卡脖网站,又有各种各样的拒绝卡脖限制,比如我一直在用的拒绝卡脖在线免费压缩工具【tinypng】,数量限制无所谓,拒绝卡脖但是拒绝卡脖图片最大5M,往往不能满足需求,拒绝卡脖如果需要使用高级功能需要付费。拒绝卡脖
当我找UI美眉帮忙把图片处理小点时,嗯哼~~~,何不自己开发一个图片处理工具。
目前软件都会上传图片,一般都会对用户上传的图片大小进行限制,并且上传之后还要压缩,这也可以为企业节省存储成本的同时,还可提高上传速率。
本文就基于SpringBoot结合thumbnailator实现图片压缩,坑已踩过拿去就用,也可以自己开发一个压缩工具不被卡脖子。
创建SpringBoot工程并引入相关依赖,根据Maven仓库地址搜索最新版本为0.4.19,因0.4.8版本利用率最高,本工程也是用0.4.8版本,你可以自行选择版本使用。
pom依赖
<!-- springboot-web --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.4</version></dependency><!-- 图片压缩依赖 --><dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.8</version></dependency><!-- 测试模块 --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId></dependency>
上传文件时接口使用MultipartFile接收文件,所以我们压缩图片的方法也接收MultipartFile类型数据,并返回MultipartFile类型的数据【可根据实际情况调整参数类型】。
package com.stt.thumbnailator.util;import lombok.extern.slf4j.Slf4j;import net.coobird.thumbnailator.Thumbnails;import org.springframework.mock.web.MockMultipartFile;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;/** * 图片处理工具类 */@Component@Slf4jpublic class ImgUtil { /** * 压缩图片,不修改尺寸 * 1、接收 file 源文件,并判断是否为空 * 2、不修改原尺寸,按比例压缩图片 * 3、将压缩后的图片封装为 MultipartFile 类型返回 * @param file * @return */ public MultipartFile zipImg(MultipartFile file) { // 判空,并且大于20kb再压缩 if(file == null || file.getSize() <= 20 * 1024) { return file; } // 根据输入流压缩 log.info("压缩前图片大小===》{ }",file.getSize()); // 字节文件输出流,保存转换后的图片数据流 ByteArrayOutputStream outputStream = null; // 通过输入流转换为 MultipartFile ByteArrayInputStream inputStream = null; try { outputStream = new ByteArrayOutputStream(); Thumbnails.of(file.getInputStream()) .scale(1f) //按比例放大缩小 和size() 必须使用一个 不然会报错 .outputQuality(0.5f) //输出的图片质量 0~1 之间,否则报错 .toOutputStream(outputStream); //图片输出位置 // 将 outputStream 转换为 MultipartFile byte[] bytes = outputStream.toByteArray(); inputStream = new ByteArrayInputStream(bytes); // 创建 MockMultipartFile 对象,该类在【spring-test】依赖中 MockMultipartFile outFile = new MockMultipartFile(file.getOriginalFilename(), file.getOriginalFilename(), file.getContentType(), inputStream); log.info("压缩后图片大小===》{ }",outFile.getSize()); // 返回图片 return outFile; } catch (IOException e) { log.error("图片压缩失败===》{ }",e); throw new RuntimeException(e); }finally { // 关闭流 try { outputStream.close(); inputStream.close(); } catch (IOException e) { } } }}
package com.stt.thumbnailator.controller;import com.stt.thumbnailator.util.ImgUtil;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;/** * 上传文件接口 */@RestController@RequestMapping("upload")public class UploadController { private final ImgUtil imgUtil; public UploadController(ImgUtil imgUtil) { this.imgUtil = imgUtil; } /** * 上传图片,请求方式需要是POST请求 * @param file */ @PostMapping("img") public String uploadImg(MultipartFile file) { // 上传图片 MultipartFile zipImg = imgUtil.zipImg(file); return "压缩成功"; }}
修改配置类默认文件上传大小,默认1M根本不够用。
spring: servlet: multipart: # 单个文件最大大小,默认1MB,该值根据实际需求调整 max-file-size: 5MB # 一次请求文件最大大小,默认10MB,该值根据实际需求调整 max-request-size: 20MB
通过apifox快速发送请求,你也可以使用postman等工具。
发现控制台输出压缩前和压缩后文件大小,压缩将近3倍,由outputQuality方法参数控制,值越小压缩越严重,当然要保障图片的清晰度,和产品需求适当调整。
亲测在 0.3 时图片会出现模糊
图片一般都会存储到文件服务器,这里暂时将图片通过IO流存储到本地,来看一下图片清晰度。
/** * 压缩图片,不修改尺寸,存储到本地 * @param file*/public void zipImgToLocation(MultipartFile file) { // 判空,并且大于20kb再压缩 if(file == null || file.getSize() <= 20 * 1024) { return; } // 字节文件输出流,保存转换后的图片数据流 ByteArrayOutputStream outputStream = null; // 获取文件名 String originalFilename = file.getOriginalFilename(); // 获取文件后缀 String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); // 存储文件位置和名字,改名字一般都随机生成 File localFile = new File("zipImg." + fileType); try { outputStream = new ByteArrayOutputStream(); Thumbnails.of(file.getInputStream()) .scale(1f) //按比例放大缩小 和size() 必须使用一个 不然会报错 .outputQuality(0.5f) //输出的图片质量 0~1 之间,否则报错 .toFile(localFile); } catch (IOException e) { log.error("图片压缩失败===》{ }",e); throw new RuntimeException(e); }finally { // 关闭流 try { outputStream.close(); } catch (IOException e) { } }}
在个人电脑前看着并没有差别,从3.68M压缩到1.05M,这张图上传到头条之后估计还得被平台压缩,总之可以根据outputQuality参数调整压缩程度,别太模糊就行。
压缩也有坑啊,比如下方代码,如果你将一张jpg的图片按照原0.5的压缩比压缩后通过outputFormat方法转换成png格式,图片反而会被增大。
/** * 压缩图片,不修改尺寸,修改为png格式 * @param file*/public void zipImgToPng(MultipartFile file) { // 判空,并且大于20kb再压缩 if(file == null || file.getSize() <= 20 * 1024) { return; } // 根据输入流压缩 log.info("压缩前图片大小===》{ }",file.getSize()); // 字节文件输出流,保存转换后的图片数据流 ByteArrayOutputStream outputStream = null; try { outputStream = new ByteArrayOutputStream(); Thumbnails.of(file.getInputStream()) .scale(1f) //按比例放大缩小 和size() 必须使用一个 不然会报错 .outputQuality(0.5f) //输出的图片质量 0~1 之间,否则报错 .outputFormat("png") // 修改图片为png格式 .toOutputStream(outputStream); log.info("压缩后图片大小===》{ }",outputStream.size()); } catch (IOException e) { log.error("图片压缩失败===》{ }",e); throw new RuntimeException(e); }finally { // 关闭流 try { outputStream.close(); } catch (IOException e) { } }}
图片由3M增大到了20M
相似的问题,不少小伙伴也都遇到了
该问题在2022年12月31号更新的0.4.19版本中也并没有解决,所以只能自行处理,也就有大佬总结出以下规则,Thumbnails.scale效果会导致图片大小变大。
根据多次测试得来的结果:用jpg转成jpg效果最佳
比如有以下水印,添加到任意一张图片的右上角
工具类
/** * 添加水印 * file: 原图 * markFile:水印*/public void addWatermark(MultipartFile file,MultipartFile markFile) { // 判空 if(file == null) { return; } // 原图文件流 try { InputStream inputStream = file.getInputStream(); InputStream markFileInputStream = markFile.getInputStream(); // 读取数据 BufferedImage srcImg = ImageIO.read(inputStream); BufferedImage markImg = ImageIO.read(markFileInputStream); //原图的宽高 int srcWidth = srcImg.getWidth(null); int srcHeight = srcImg.getHeight(null); //水印图片的宽高 int markWidth = markImg.getWidth(null); int markHeight = markImg.getHeight(null); //计算输出水印图片的位置x和y轴 int mark_x = srcWidth - srcWidth / 9; int mark_y = srcWidth / 9-srcWidth / 10; //计算输出水印图片的大小 int mark_width = srcWidth / 10; int mark_height = (srcWidth * markHeight) / (10 * markWidth); //将水印图片压缩成输出的大小 markImg = Thumbnails.of(markImg).size(mark_width,mark_height).asBufferedImage(); String originalFilename = file.getOriginalFilename(); // 获取文件后缀 String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); File finalFile = new File("finalFile." + fileType); //watermark(位置,水印图,透明度0.5f=50%透明度) //outputQuality(控制图片的质量,1f=100%高质量) Thumbnails.of(srcImg) .size(srcWidth, srcHeight) .watermark(new Coordinate(mark_x,mark_y), markImg, 1f) .outputQuality(1f) .toFile(finalFile); } catch (IOException e) { throw new RuntimeException(e); }}
接口需要接收两张图片,你也尽可以使用多文件上传
@PostMapping("img/addWatermark")public String addWatermark(@RequestParam("srcFile") MultipartFile srcFile,@RequestParam("markFile") MultipartFile markFile) { // 上传图片 imgUtil.addWatermark(srcFile,markFile); return "添加水印成功";}
测试添加水印,发送请求时参数名一定要和接口中定义的@RequestParam 注解值相同
测试后发现右上角确实添加上了水印
可以通过 rotate 方法在顺时针和逆时针方向旋转图片
/** * 旋转图片*/public void rotate(MultipartFile file) { try { String originalFilename = file.getOriginalFilename(); // 获取文件后缀 String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); File finalFile = new File("finalFile." + fileType); Thumbnails.of(file.getInputStream()) .rotate(90) // 角度,正数:顺时针,负数:逆时针 .toFile(finalFile); } catch (IOException e) { throw new RuntimeException(e); }}
这就是我在使用的图片处理技术,当然也有一些其他的方案,与其寻找各种方案哪种最优,不如挑选一种使用,进而自行优化,选择技术的路上往往会浪费许多时间,你觉得呢?
责任编辑:武晓燕 来源: 今日头条 图片压缩工具(责任编辑:综合)
中国海油牵头签订国内最大规模液化天然气船舶建造项目 建造金额约160亿元
江西省一季度国有经济亮出成绩单 国有企业资产规模达到6.1万亿元