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

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

文章简介

有时候,我们项目需求,需要对业务图片生成小的缩略图,或者是对图片格式进行转码,大家会使用什么方法呢?

直接使用对象存储,把业务需求交给第三方?还是用javaScript对图片进行操作?

有没有试过后端直接处理呢?

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

如果你想支持更多图片,可以扩展Java图片IO流,这个以后再说……

视频教程

简单录制了一个视频,有需要可以转至b站:

6分钟学会使用Java“硬核”压缩和转码图片--图片转码和缩略图生成

图片转码/生成缩略图

原理

本次使用的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进行处理的。除非能直接写一个转码器。但是这样,我觉得还不如调用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 APIBufferedImagesGraphics2D来处理图片,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项目支持更多图片格式。

猜你喜欢

转载自juejin.im/post/7055461293456621582