「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
文章简介
有时候,我们项目需求,需要对业务图片生成小的缩略图,或者是对图片格式进行转码,大家会使用什么方法呢?
直接使用对象存储,把业务需求交给第三方?还是用javaScript对图片进行操作?
有没有试过后端直接处理呢?
本文中介绍,如何使用Java优雅处理图片;包括:主流图片格式转码、图片压缩(缩略图生成)、图片附带水印等。主要用到的外部工具包:
- Thumbnailator:老牌缩略图生成工具。
- webp-imageio-core:让Java支持Webp的读写。
如果你想支持更多图片,可以扩展Java图片IO流,这个以后再说……
视频教程
简单录制了一个视频,有需要可以转至b站:
6分钟学会使用Java“硬核”压缩和转码图片--图片转码和缩略图生成
图片转码/生成缩略图
原理
本次使用的Thumbnailator包,实际上是封装好的类和方法,基于Java的Image I/O API
、Java 2D API
等API接口实现。
所以,因为基于Java Image I/O API,所以支持的图片格式有限,但是已经满足绝大多数情况。 一般支持的格式如下:
Read
:JPEG 2000, JPG, tiff, bmp, PCX, gif, WBMP, PNG, RAW, JPEG, PNM, tif, TIFF, wbmp, jpeg, jbig2, jpg, JPEG2000, BMP, pcx, GIF, png, raw, JBIG2, pnm, TIF, jpeg2000, jpeg 2000Write
:JPEG 2000, JPG, tiff, bmp, PCX, gif, WBMP, PNG, RAW, JPEG, PNM, tif, TIFF, wbmp, jpeg, jpg, JPEG2000, BMP, pcx, GIF, png, raw, pnm, TIF, jpeg2000, jpeg 2000
很多文章作者,介绍这个包,居然还说支持Apple的
HEIC
格式,这个是肯定不支持使用Thumbnailator
进行处理的。除非能直接写一个转码器。但是这样,我觉得还不如调用ImageMagick。这个以后有机会给大家分享。
如何安装
首先添加lib
包,如果你是Maven工程,或者使用Maven管理的项目,添加依赖:
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>[0.4, 0.5)</version>
</dependency>
复制代码
上面的依赖项定义是获取0.4.x版本范围内的Thumbnailator
的最新可用版本。如果需要特定版本的Thumbnailator,则将[0.4,0.5)替换为特定版本号,例如0.4.13
另外,如果下载太慢,可以把Maven换成国内下载源(比如:阿里Maven镜像源)
如果你不是Maven工程,可以下载Thumbnailator
的最新版本,如何手动添加lib包,最新版本Thumbnailator
下载:github.com/coobird/thu…
如何使用
Thumbnailator
的使用十分简单,原本你需要使用Java的Image I/O API
、BufferedImages
和Graphics2D
来处理图片,Thumbnailator
直接封装上述操作。简单的使用演示:
Thumbnails.of(new File("path/to/directory").listFiles())
.size(640, 480)
.outputFormat("jpg")
.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
复制代码
- 原图片地址:
path/to/directory
- 输出图片大小:
640*480
- 输出图片格式:
jpg
- IO流输出地址(输出图片):
Rename.PREFIX_DOT_THUMBNAIL
图片转码
演示代码:
Thumbnails.of(originalImage).scale(scale)
.outputFormat("jpg")
.outputQuality(compression)
.toFile(thumbnailImage);
复制代码
其中:
scale
是图片尺寸等比缩放,为float
类型。outputFormat
是输出图片的类型,注意:默认不支持webp
,如果需要使用webp
,需要提前安装webp-imageio-core,可以看看下文如何使Java支持Webp
。outputQuality
是输出图片的质量,即:清晰度/分辨率。
使用原图片生成缩略图
演示代码:
Thumbnails.of(new File("original.jpg"))
.size(160, 160)
.toFile(new File("thumbnail.jpg"));
复制代码
其中,原图片文件,可以使用字符串String
来代替地址:
Thumbnails.of("original.jpg")
.size(160, 160)
.toFile("thumbnail.jpg");
复制代码
通常,缩略图输出体积已经很小,但是还是可以使用.outputQualit
来降低图片质量(分辨率)。
旋转图片
很简单;添加.rotate
即可。如:
Thumbnails.of(new File("original.jpg"))
.rotate(90)
.toFile(new File("image-with-watermark.jpg"));
复制代码
添加水印
添加水印也十分简单,添加.watermark
即可:
Thumbnails.of(new File("original.jpg"))
.watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)
.toFile(new File("image-with-watermark.jpg"));
复制代码
实操演示
我在我网站上使用上述包,搭建了一个在线演示地址:tool.mintimate.cn/processIMG
功能:用户上传图片后,系统更具用户的选择的输出格式,转码图片。
扩展名判断
前端传送图片到后台,我们后台可以对文件扩展名进行判断:
String thumbnailImageName=originalImage.getName(); //缩略图输出名
String thumbnailImagePath; //缩略图输出地址
switch (format){
case "JPG":
thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
+ thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".jpg";
break;
case "PNG":
thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
+ thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".png";
break;
case "WEBP":
thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
+ thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".webp";
break;
case "BMP":
thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
+ thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".bmp";
break;
default:
thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/" + thumbnailImageName;
break;
}
复制代码
创建空白缩略图文件
虽然Thumbnailator
可以直接自动根据String
创建对应文件对象,但是为了更方便我们自己控制,我们手动创建:
File thumbnailImage = new File(thumbnailImagePath);
// 判断路径是否存在,如果不存在则创建
if (!thumbnailImage.getParentFile().exists()) {
thumbnailImage.getParentFile().mkdirs();
}
复制代码
转码图片
try {
switch (format){
case "JPG":
Thumbnails.of(originalImage).scale(scale)
.addFilter(new ThumbnailsImgFilter())
.outputFormat("jpg")
.outputQuality(compression)
.toFile(thumbnailImage);
break;
case "PNG":
Thumbnails.of(originalImage).scale(scale)
.outputFormat("png")
.outputQuality(compression)
.toFile(thumbnailImage);
break;
case "WEBP":
Thumbnails.of(originalImage).scale(scale)
.imageType(ThumbnailParameter.DEFAULT_IMAGE_TYPE)
.outputFormat("webp")
.outputQuality(compression)
.toFile(thumbnailImage);
break;
case "BMP":
Thumbnails.of(originalImage).scale(scale)
.addFilter(new ThumbnailsImgFilter())
.outputFormat("bmp")
.outputQuality(compression)
.toFile(thumbnailImage);
break;
default:
Thumbnails.of(originalImage).scale(scale)
.imageType(ThumbnailParameter.DEFAULT_IMAGE_TYPE)
.outputQuality(compression)
.toFile(thumbnailImage);
break;
}
} catch (IOException e) {
e.printStackTrace();
}
复制代码
因为,我是使用Springboot
快速构建,我其实是创建了一个配置规则addFilter
,可以使PNG
透明图片转为JPG
时,透明背景渲染为白色。(单纯为了好看……)。实现细节:
import net.coobird.thumbnailator.filters.ImageFilter;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ThumbnailsImgFilter implements ImageFilter {
@Override
public BufferedImage apply(BufferedImage bufferedImage) {
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = newImage.createGraphics();
graphic.setColor(Color.white);//背景设置为白色
graphic.fillRect(0, 0, w, h);
graphic.drawRenderedImage(bufferedImage, null);
graphic.dispose();
return newImage;
}
}
复制代码
这样,就可以成功转码图片了(运用恰当,还可以压缩图片( ;´Д`)):
左边为原图,右边为转码后图片。这个是大小不变情况下,图片质量变为原来80%;主要文件大小变小,是Webp格式带来的。下文我们介绍一下Java转码Webp格式。
Java处理Webp格式
什么是Webp格式
根据Wiki百科:WebP(发音:weppy])是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生自影像编码格式,被认为是WebM多媒体格式的姊妹项目,是由Google在购买On2 Technologies后发展出来,以BSD授权条款发布。 而Webp具有的优势,显而易见:
- 更优的图像数据压缩算法
- 更小的图片体积
- 肉眼识别无差异的图像质量
- 无损和有损的压缩模式
- Alpha 透明以及动画的特性
简单地说,它可以像PNG
格式一样,保存无损画质,并保持图片透明特性;同时,可以像JPG
一样,压缩图片。Webp
在同等情况下,文件体积比PNG
小,甚至比JPG
还小。
支持Webp格式
因为Webp,实际上是Google开发的,所以Java IO流
设计之初就不支持Webp格式。
让Java IO流支持Webp的方法很多,这里介绍一种方法,有机会接受更多方法~
根据系统的不同,需要安装对应的依赖包:
/natives
/linux_32
libxxx[-vvv].so
/linux_64
libxxx[-vvv].so
/osx_32
libxxx[-vvv].dylib
/osx_64
libxxx[-vvv].dylib
/osx_arm64
libxxx[-vvv].dylib
/windows_32
xxx[-vvv].dll
/windows_64
xxx[-vvv].dll
/aix_32
libxxx[-vvv].so
libxxx[-vvv].a
/aix_64
libxxx[-vvv].so
libxxx[-vvv].a
复制代码
可以参考项目:github.com/scijava/nat… 当然,你也可以直接用大神整合好的lib包,比如:webp-imageio-core;下文就详解如何使用。
webp-imageio-core使用
因为webp-imageio-core
并没有发布到Maven中央仓库,所以使用Maven骨架用户需要自己添加lib
依赖 首先下载webp-imageio-core
的jar发布包,下载地址:github.com/nintha/webp… 之后添加自定义<dependency>
:
<dependency>
<groupId>com.github.nintha</groupId>
<artifactId>webp-imageio-core</artifactId>
<version>{version}</version>
<scope>system</scope>
<systemPath>${pom.basedir}/libs/webp-imageio-core-{version}.jar</systemPath>
</dependency>
复制代码
比如:我的项目,添加本地lib
:
这个时候,Java就已经支持处理Webp
格式图片了。
实操使用
最简单的使用……其实是再加入上文所提到的Thumbnailator
依赖包,便可以使用Thumbnailator
直接处理图片IO流。 单独使用,我们可以用最传统的方法处理: 图片转WEBP:
public static void main(String args[]){
String srcFile = System.getProperty("user.dir") + "/file/Input/"+"Input.png" //原图地址
String webpFile = System.getProperty("user.dir") + "/file/Output/"+"Output.png" //输出地址
encodingToWebp(srcFile, webpFile);
}
public static void encodingToWebp(String srcFile, String webpFile) {
encodingToWebp(new File(srcFile),new File(webpFile) );
}
/**
* @param: srcFile
* @param: webpFile
* @description: 将文件编码为WEBP格式
* @author: Mintimate
*/
public static void encodingToWebp(File srcFile, File webpFile) {
try {
// Obtain an image to encode from somewhere
BufferedImage image = ImageIO.read(srcFile);
// Obtain a WebP ImageWriter instance
ImageWriter writer = ImageIO.getImageWritersByMIMEType("image/webp").next();
// Configure encoding parameters
WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType(writeParam.getCompressionTypes()[WebPWriteParam.LOSSLESS_COMPRESSION]);
// Configure the output on the ImageWriter
writer.setOutput(new FileImageOutputStream(webpFile));
// Encode
writer.write(null, new IIOImage(image, null, null), writeParam);
//释放reader
writer.dispose();
//关闭文件流
fileImageOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
复制代码
最后
到此,使用Thumbnailator就可以优雅地处理图片了。
但是你会发现,有些情况图片处理(jpeg图片处理时)会偏红,处理方法很简单,可以用BufferedImage
去写图片和读图片,这样就不会出现图片偏红或偏粉的失真情况。
有机会接受其他Java图片IO扩展包,让Java项目支持更多图片格式。