本文中介绍,如何使用Java优雅处理图片;包括:主流图片格式转码、图片压缩(缩略图生成)等。主要用到的外部工具包:
本文同步博客图文( ´▽`):https://www.mintimate.cn/2021/01/19/JavaDealWithImage
同时,同步发布在腾讯云社区:https://cloud.tencent.com/developer/article/1779344
搭建了一个在线演示地址:https://image.mintimate.cn/
和以前一样,我们觉得有用、有必要录个视频的内容,我们还是会花时间做个视频:
B站视频:视频原链接,望投币嗷:https://www.bilibili.com/video/BV1nT4y1K7E6
腾讯云社区视频:视频链接地址,望点赞嗷:https://cloud.tencent.com/developer/video/10496
本次博客使用的Thumbnailator包,实际上是封装好的类和方法,基于Java的Image I/O API
、Java 2D API
等API接口实现。所以,因为基于Java Image I/O API,所以支持的图片格式有限,但是已经满足绝大多数情况。
一般支持的格式如下:
所以,像Apple的HEIC
格式,是不支持使用Thumbnailator
进行处理的。
首先添加lib
包,如果你是Maven工程,或者使用Maven管理的项目,添加依赖:
net.coobird
thumbnailator
[0.4, 0.5)
上面的依赖项定义是获取0.4.x版本范围内的Thumbnailator
的最新可用版本。如果需要特定版本的Thumbnailator,则将[0.4,0.5)替换为特定版本号,例如0.4.13
另外,如果下载太慢,可以把Maven换成国内下载源(比如:阿里Maven镜像源)
如果你不是Maven工程,可以下载Thumbnailator
的最新版本,如何手动添加lib包,最新版本Thumbnailator
下载:https://github.com/coobird/thumbnailator/releases/latest
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
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"));
我在我网站上使用上述包,搭建了一个在线演示地址:https://image.mintimate.cn/
功能:用户上传图片后,系统更具用户的选择的输出格式,转码图片。
前端传送图片到后台,我们后台可以对文件扩展名进行判断:
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格式。
根据Wiki百科:WebP(发音:weppy])是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生自影像编码格式,被认为是WebM多媒体格式的姊妹项目,是由Google在购买On2 Technologies后发展出来,以BSD授权条款发布。
而Webp具有的优势,显而易见:
PNG
格式一样,保存无损画质,并保持图片透明特性;同时,可以像JPG
一样,压缩图片。Webp
在同等情况下,文件体积比PNG
小,甚至比JPG
还小。因为Webp,实际上是Google开发的,所以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
可以参考项目:https://github.com/scijava/native-lib-loader
当然,你也可以直接用大神整合好的lib包,比如:webp-imageio-core;下文就详解如何使用。
因为webp-imageio-core
并没有发布到Maven中央仓库,所以使用Maven骨架用户需要自己添加lib
依赖
首先下载webp-imageio-core
的jar发布包,下载地址:https://github.com/nintha/webp-imageio-core/releases
之后添加自定义
:
com.github.nintha
webp-imageio-core
{version}
system
${pom.basedir}/libs/webp-imageio-core-{version}.jar
比如:我的项目,添加本地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();
}
}
感兴趣可以关注B站:Mintimate;
如果想进一步处理图片,需要基于系统工具包了:
比如:ImageMagick
ImageMagick其实是真正全功能图片处理,但是Java调用前,系统需要预先安装ImageMagick,且Mac、Linux和Windows上调用方法有差异,有机会给大家介绍( ̀⌄ ́)