使用Java优雅地处理图片——图片转码和缩略图生成

本文最后更新于:2021年5月8日 早上

文章简介

本文中介绍,如何使用Java优雅处理图片;包括:主流图片格式转码、图片压缩(缩略图生成)等。主要用到的外部工具包:

在线演示

搭建了一个在线演示地址:https://image.mintimate.cn/

视频教程

和以前一样,我们觉得有用、有必要录个视频的内容,我们还是会花时间做个视频:

感觉有用,可以给视频投个币么?( ´▽`):视频原链接
如果B站播放有困难,我在腾讯云社区也有发布视频:视频链接地址,望点赞嗷

图片转码/生成缩略图

原理

本次博客使用的Thumbnailator包,实际上是封装好的类和方法,基于Java的Image I/O APIJava 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 2000
  • Write: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进行处理的。

如何安装

首先添加lib包,如果你是Maven工程,或者使用Maven管理的项目,添加依赖:

1
2
3
4
5
<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下载:https://github.com/coobird/thumbnailator/releases/latest

如何使用

Thumbnailator的使用十分简单,原本你需要使用Java的Image I/O APIBufferedImagesGraphics2D来处理图片,Thumbnailator直接封装上述操作。简单的使用演示:

1
2
3
4
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

图片转码

演示代码:

1
2
3
4
Thumbnails.of(originalImage).scale(scale)
.outputFormat("jpg")
.outputQuality(compression)
.toFile(thumbnailImage);

其中:

  • scale是图片尺寸等比缩放,为float类型。
  • outputFormat是输出图片的类型,注意:默认不支持webp,如果需要使用webp,需要提前安装webp-imageio-core,可以看看下文如何使Java支持Webp
  • outputQuality是输出图片的质量,即:清晰度/分辨率。

使用原图片生成缩略图

演示代码:

1
2
3
Thumbnails.of(new File("original.jpg"))
.size(160, 160)
.toFile(new File("thumbnail.jpg"));

其中,原图片文件,可以使用字符串String来代替地址:

1
2
3
Thumbnails.of("original.jpg")
.size(160, 160)
.toFile("thumbnail.jpg");

通常,缩略图输出体积已经很小,但是还是可以使用.outputQualit来降低图片质量(分辨率)。

旋转图片

很简单;添加.rotate即可。如:

1
2
3
Thumbnails.of(new File("original.jpg"))
.rotate(90)
.toFile(new File("image-with-watermark.jpg"));

添加水印

添加水印也十分简单,添加.watermark即可:

1
2
3
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/

功能:用户上传图片后,系统更具用户的选择的输出格式,转码图片。

扩展名判断

前端传送图片到后台,我们后台可以对文件扩展名进行判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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创建对应文件对象,但是为了更方便我们自己控制,我们手动创建:

1
2
3
4
5
File thumbnailImage = new File(thumbnailImagePath);
// 判断路径是否存在,如果不存在则创建
if (!thumbnailImage.getParentFile().exists()) {
thumbnailImage.getParentFile().mkdirs();
}

转码图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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时,透明背景渲染为白色。(单纯为了好看……)。实现细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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还小。

Java如何支持Webp格式

因为Webp,实际上是Google开发的,所以Java IO流设计之初就不支持Webp格式。所以根据系统的不同,需要安装对应的依赖包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/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使用

因为webp-imageio-core并没有发布到Maven中央仓库,所以使用Maven骨架用户需要自己添加lib依赖
首先下载webp-imageio-core的jar发布包,下载地址:https://github.com/nintha/webp-imageio-core/releases
之后添加自定义<dependency>:

1
2
3
4
5
6
7
<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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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


若对文章很感兴趣,可以B站关注我ヾ(≧▽≦*)o

点击图片跳转“关于”页面(○` 3′○)