https://mvnrepository.com/artifact/net.coobird/thumbnailator
humbnailator 是一个开源的 Java 项目,它提供了非常简单的 API 来对图片进行缩放、旋转以及加水印的处理。
<dependency>
<groupId>net.coobirdgroupId>
<artifactId>thumbnailatorartifactId>
<version>0.4.17version>
dependency>
<dependency>
<groupId>com.madgaggroupId>
<artifactId>animated-gif-libartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>com.twelvemonkeys.imageiogroupId>
<artifactId>imageio-jpegartifactId>
<version>3.9.4version>
dependency>
<dependency>
<groupId>com.twelvemonkeys.imageiogroupId>
<artifactId>imageio-tiffartifactId>
<version>3.9.4version>
dependency>
import com.zkjw.cms.common.utils.token.Base64;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
/***
* 对图片进行操作
* @author liuhongrong
* @since 2011/7/29
*
*/
public class ImagesUtil {
private static Logger logger= LoggerFactory.getLogger(ImagesUtil.class);
/**
* 允许上传图片格式
*/
public static String[] ImagesType=new String[] {"png", "jpg", "jpeg", "bmp", "gif","svg"};
/**
* 根据扩展名判断是否为图片
*/
public static boolean extCheckImages(String fileNamefix){
if(StringUtils.isNotEmpty(fileNamefix)){
return ArrayUtil.contains(ImagesType,fileNamefix);
}else{
return false;
}
}
/**
* 添加水印
* @param originImgPath 路径
* @param positions 水印位置
* @param markImgPath 水印图片
*/
public static boolean markWater(String originImgPath,Positions positions,String markImgPath){
//准备画板
BufferedImage originImg = null; //判断原图是否存在
BufferedImage markImg = null; //判断水印是否存在
try {
originImg = ImageIO.read(new File(originImgPath));
markImg = ImageIO.read(new File(markImgPath));
Thumbnails.of(originImgPath).size(originImg.getWidth(),originImg.getHeight()).watermark(positions, markImg, 0.8f)
.outputQuality(1f).toFile(originImgPath);
return true;
} catch (IOException e) {
logger.error("图片添加水印",e);
return false;
}
}
/***
* 将图片缩放到指定的高度或者宽度
* @param sourceImagePath 图片源地址
* @param destinationPath 压缩完图片的地址
* @param width 缩放后的宽度
* @param fileType 图片类型
*/
public static void scaleImageWithParams(String sourceImagePath,String destinationPath, Integer width, String fileType){
if(("gif").equals(fileType)) {
/**
* gif压缩图片,压缩gif越来越大不做处理
*/
// ImagesGifOperatorUtil.scaleImageWithParams(new File(sourceImagePath),new File(destinationPath), width);
}else {
try {
sourceImagePath = FileUtil.getpathurl(sourceImagePath, 1);
//判断是否为图片文件
if (getispic(sourceImagePath)) {
BufferedImage bufferedImage = ImageIO.read(new File(sourceImagePath));
int height = bufferedImage.getHeight();
if (width == null) {
width = bufferedImage.getWidth();
}
//获取位深度
ColorModel color = bufferedImage.getColorModel();
if (color.getPixelSize() <= 16) {
zoomImageScale(sourceImagePath, destinationPath, width);
} else {
/*
* size(width,height) 若图片横比200小,高比300小,不变
* 若图片横比200小,高比300大,高缩小到300,图片比例不变 若图片横比200大,高比300小,横缩小到200,图片比例不变
* 若图片横比200大,高比300大,图片按比例缩小,横为200或高为300
* outputQuality: 输出的图片质量,范围:0.0~1.0,1为最高质量.
*/
Thumbnails.of(sourceImagePath).size(width, height).outputQuality(1.0f).toFile(destinationPath);
}
} else {
logger.error(sourceImagePath + "不是一张有效图片");
}
} catch (Exception e) {
logger.error("发布时:将图片缩放到指定的高度或者宽度报错", e);
}
}
}
/***
* 将图片缩放到指定的高度或者宽度
* @param sourceImagePath 图片源地址
* @param destinationPath 压缩完图片的地址
* @param width 缩放后的宽度
* @param fileType 图片类型
*/
public static void scaleImageWithParams(File sourceImagePath,File destinationPath, Integer width, String fileType){
if(("gif").equals(fileType)) {
/**
* gif压缩图片,压缩gif越来越大不做处理
*/
// ImagesGifOperatorUtil.scaleImageWithParams(sourceImagePath,destinationPath, width);
}else{
boolean f = false;
BufferedImage bufferedImage=null;
//判断是否为图片文件
try {
bufferedImage= ImageIO.read(sourceImagePath);
if(bufferedImage == null){
f = false;
}else {
f = true;
}
} catch (IOException e) {
logger.error(sourceImagePath+"不是一张有效图片",e);
}
try{
if(f){
int height=bufferedImage.getHeight();
if(width == null){
width=bufferedImage.getWidth();
}
//获取位深度
ColorModel color = bufferedImage.getColorModel();
if(color.getPixelSize() <= 16){
zoomImageScale(sourceImagePath.getPath(), destinationPath.getPath(), width);
}else{
/*
* size(width,height) 若图片横比200小,高比300小,不变
* 若图片横比200小,高比300大,高缩小到300,图片比例不变 若图片横比200大,高比300小,横缩小到200,图片比例不变
* 若图片横比200大,高比300大,图片按比例缩小,横为200或高为300
* outputQuality: 输出的图片质量,范围:0.0~1.0,1为最高质量.
*/
Thumbnails.of(sourceImagePath).size(width,height).outputQuality(1.0f).toFile(destinationPath);
}
}
}catch (Exception e){
logger.error("发布时:将图片缩放到指定的高度或者宽度报错",e);
}
}
}
/**
* 判断是否为图片文件
* @param filePath
* @return
*/
public static boolean getispic(String filePath){
boolean f = false;
File file = new File(filePath);
try {
BufferedImage bi = ImageIO.read(file);
if(bi == null){
f = false;
}else {
f = true;
}
} catch (IOException e) {
f = false;
}
return f;
}
/**
* 处理 图片位深度:8位的图片,会自动压缩成32位深度的图片
* 按指定宽度 等比例缩放图片
* @param oldPath 需要压缩图片的路径
* @param newPath 新路径
* @param newWidth 新图的宽度
* @throws IOException
*/
public static void zoomImageScale(String oldPath, String newPath, int newWidth) throws IOException {
File imageFile =new File(oldPath);
if (!imageFile.canRead()){
return;
}
BufferedImage bufferedImage = ImageIO.read(imageFile);
if (null == bufferedImage){
return;
}
int originalWidth = bufferedImage.getWidth();
int originalHeight = bufferedImage.getHeight();
double scale = (double) originalWidth / (double) newWidth; // 缩放的比例
int newHeight = (int) (originalHeight / scale);
zoomImageUtils(imageFile, newPath, bufferedImage, newWidth, newHeight);
}
private static void zoomImageUtils(File imageFile, String newPath, BufferedImage bufferedImage, int width, int height) throws IOException {
String suffix = StringUtils.substringAfterLast(imageFile.getName(), ".");
// 处理 png 背景变黑的问题
if (suffix != null && (suffix.trim().toLowerCase().endsWith("png") || suffix.trim().toLowerCase().endsWith("gif"))) {
BufferedImage to = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = to.createGraphics();
to = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = to.createGraphics();
Image from = bufferedImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
g2d.drawImage(from, 0, 0, null);
g2d.dispose();
ImageIO.write(to, suffix, new File(newPath));
} else {
BufferedImage newImage = new BufferedImage(width, height, bufferedImage.getType());
Graphics g = newImage.getGraphics();
g.drawImage(bufferedImage, 0, 0, width, height, null);
g.dispose();
ImageIO.write(newImage, suffix, new File(newPath));
}
}
//--------------------------------------------图片生成Base64字符串----------------------------------------------------------------
/**
* 将图片生成Base64字符串,可以直接在网页展示
* @return boolean
* @throws null
*/
public static String base64ImageHtml(String base64Data,String ext){
StringBuffer sb=new StringBuffer(1000);
sb.append("data:image/").append(ext);
sb.append(";base64,").append(base64Data);
return sb.toString();
}
/**
* 转换base64
* @param srcImagePath 要进行压缩的文件全路径
* @return 返回压缩后的文件的全路径
* @throws IOException
*/
public static String reduceWidthHeightImageAndToBase64(String srcImagePath){
try {
File file=new File(srcImagePath);
if(file.exists()){
BufferedImage bi=ImageIO.read(file);
byte[] bytes=null;
ByteArrayOutputStream out=new ByteArrayOutputStream();
bytes = out.toByteArray();
return Base64.encode(bytes);
}else{
logger.info("找不到文件:"+srcImagePath);
return StringUtil.EMPTY;
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
import com.madgag.gif.fmsware.AnimatedGifEncoder;
import com.madgag.gif.fmsware.GifDecoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author liuhongrong
* @description 压缩GIF
* @desc 压缩GIF
* @since 2023/4/10 10:20
*/
public class ImagesGifOperatorUtil {
public static void main(String[] args) throws IOException {
String outputPath = "/home/lab/test/001.gif";
String imagePath = "/home/lab/test/33.gif";
reverseGif(imagePath,outputPath, 1280,760);
}
/**
* 4096异常。在过去的几年里,处理GIF图像时可能遇到过这个问题
* @param sourceImagePath
* @param destinationPath
* @param width
*/
public static void scaleImageWithParams(File sourceImagePath,File destinationPath, Integer width) {
/**
* gif压缩图片
*/
try {
//使用hutool工具类GifDecoder读取gif获取上传图片的宽和高
GifDecoder gifDecoder = new GifDecoder();
InputStream inputStream=new FileInputStream(sourceImagePath);
gifDecoder.read(inputStream);
BufferedImage sourceImage = gifDecoder.getImage();
int height=sourceImage.getHeight();
if(width == null){
width=sourceImage.getWidth();
}
ImagesGifOperatorUtil.reverseGif(sourceImagePath.getPath(),destinationPath.getPath(),width,height);
}catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 压缩GIF
* @param imagePath
* @param outputPath
* @throws IOException
*/
public static void reverseGif(String imagePath,String outputPath, Integer width, Integer height) throws IOException {
GifDecoder decoder = new GifDecoder();
int status = decoder.read(imagePath);
if (status != GifDecoder.STATUS_OK) {
throw new IOException("read image " + imagePath + " error!");
}
// 拆分一帧一帧的压缩之后合成
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.start(outputPath);
encoder.setRepeat(decoder.getLoopCount());
for (int i = 0 ; i < decoder.getFrameCount(); i++) {
encoder.setDelay(decoder.getDelay(i));// 设置播放延迟时间
BufferedImage bufferedImage = decoder.getFrame(i);// 获取每帧BufferedImage流
// int height = bufferedImage.getHeight();
int widthsrc = bufferedImage.getWidth(); //原图片宽度
if(widthsrc <= width){ //原图片宽度小于指定的宽度
width=widthsrc;
}
BufferedImage zoomImage = new BufferedImage(width, height, bufferedImage.getType());
Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
Graphics gc = zoomImage.getGraphics();
gc.setColor(Color.WHITE);
gc.drawImage(image, 0, 0, null);
encoder.addFrame(zoomImage);
}
encoder.finish();
File outFile = new File(outputPath);
BufferedImage image = ImageIO.read(outFile);
ImageIO.write(image, outFile.getName(), outFile);
}
/**
* 多图片转gif
* @param imageList
* @param outputPath
* @throws IOException
*/
static void imagesToGif(List<BufferedImage> imageList, String outputPath) throws IOException {
// 拆分一帧一帧的压缩之后合成
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.start(outputPath);
encoder.setRepeat(0);
for (BufferedImage bufferedImage :
imageList) {
encoder.setDelay(100);
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
BufferedImage zoomImage = new BufferedImage(width, height, 3);
Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
Graphics gc = zoomImage.getGraphics();
gc.setColor(Color.WHITE);
gc.drawImage(image, 0, 0, null);
encoder.addFrame(zoomImage);
}
encoder.finish();
File outFile = new File(outputPath);
BufferedImage image = ImageIO.read(outFile);
ImageIO.write(image, outFile.getName(), outFile);
}
/**
* Gif转图片集
* @param imagePath
* @param outputDirPath
* @throws IOException
*/
static void gifToImages(String imagePath,String outputDirPath) throws IOException {
GifDecoder decoder = new GifDecoder();
int status = decoder.read(imagePath);
if (status != GifDecoder.STATUS_OK) {
throw new IOException("read image " + imagePath + " error!");
}
for (int i = 0; i < decoder.getFrameCount();i++) {
BufferedImage bufferedImage = decoder.getFrame(i);// 获取每帧BufferedImage流
File outFile = new File(outputDirPath + i + ".png");
ImageIO.write(bufferedImage, "png", outFile);
}
}
}