Java使用Thumbnails实现图片指定大小压缩
项目中有个要求,对上传服务器的图片大小进行判断,大于50k的图片要进行压缩处理,让其小于50k后在上传。针对图片压缩,一般有以下两种方法:1)java api的ImageIO实现图片压缩,但效果不好,图片压缩后出现变红现象,看了网上的博客普遍都说bug比较多,会有OOM内存溢出的现象。2)该方法使用的是谷歌的Thumbnails插件来实现图片压缩。本文主要介绍的是使用插件实现图片压缩。
实现:
Thumbnails.of("源文件路径")
.scale(1f) //0-1 float 压缩大小
.outputQuality(0.7f) //0-1 压缩质量
.toFile("新文件路径");
需要引入的依赖:
<!--图片压缩-->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
使用Thumbnails插件实现图片压缩方法有以下两种实现方式:
第一种方式,实现思路:
按照一定的比例压缩图片,递归压缩图片,如果压缩后的图片还不满足要求,则继续进行压缩,直到压缩后的图片大小满足要求。
代码实现:
public class ThumbnailUtil {
/**
* 根据指定大小和指定经度压缩图片
*
* @param srcPath 源图片地址
* @param desPath 目标图片地址
* @param desFileSize 指定图片大小,单位kb
* @param accuracy 精度,递归压缩的比率,建议小于0.9
* @return
*/
public static String compressPictureForScale(String srcPath, String desPath, long desFileSize, double accuracy) {
if (StringUtils.isEmpty(srcPath) || StringUtils.isEmpty(desPath)) {
return null;
}
if (!new File(srcPath).exists()) {
return null;
}
try {
File srcFile = new File(srcPath);
long srcFileSize = srcFile.length();
System.out.println("源图片: " + srcPath + ", 大小: " + srcFileSize / 1024 + "kb");
//1.先转换成jpg
Thumbnails.of(srcPath).scale(1f).toFile(desPath);
//递归压缩,直到目标文件大小小于desFileSize
compressPicCycle(desPath, desFileSize, accuracy);
File desFile = new File(desPath);
System.out.println("目标图片: " + desPath + ", 大小: " + desFile.length() / 1024 + "kb");
System.out.println("图片压缩完成!");
} catch (Exception e) {
e.printStackTrace();
return null;
}
return desPath;
}
private static void compressPicCycle(String desPath, long desFileSize, double accuracy) throws IOException {
File srcFileJPG = new File(desPath);
long srcFileSizeJPG = srcFileJPG.length();
//2.判断大小,如果小于50kb,不用压缩,如果大于等于50kb,需要压缩
if (srcFileSizeJPG <= desFileSize * 1024) {
return;
}
//计算宽高
BufferedImage bim = ImageIO.read(srcFileJPG);
int srcWidth = bim.getWidth();
int srcHeight = bim.getHeight();
int destWidth = new BigDecimal(srcWidth).multiply(new BigDecimal(accuracy)).intValue();
int destHeight = new BigDecimal(srcHeight).multiply(new BigDecimal(accuracy)).intValue();
Thumbnails.of(desPath).size(destWidth,destHeight).outputQuality(accuracy).toFile(desPath);
compressPicCycle(desPath,desFileSize,accuracy);
}
public static void main(String[] args) {
String srcPath = "C:\\Users\\hemanman6\\Desktop\\微信图片_20210802151348.png";
String desPath = "C:\\Users\\hemanman6\\Desktop\\2.jpg";
ThumbnailUtil.compressPictureForScale(srcPath,desPath,50,0.8);
}
}
结果显示:
注意:
png转jpg图片占存大小变小,jgp转png图片占存大小变大
为了更贴近公司的业务代码,对上述代码进行改进,去除了一些多余的IO过程,把递归改成了循环,并且把文件操作改为了流和字节数组的操作。
第二种,使用循环方式、流、字节数组实现
代码实现如下:
public class ThumbnailUtis1 {
private static Logger logger = LoggerFactory.getLogger(ThumbnailUtis1.class);
/**
* 根据指定大小压缩图片
*
* @param imageBytes 源图片字节数组
* @param desFileSize 指定图片大小,单位kb
* @param imageId 影像编号
* @return 压缩质量后的图片字节数组
*/
public static byte[] compressPictureForScale(byte[] imageBytes, long desFileSize, String imageId) {
if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < desFileSize * 1024) {
return imageBytes;
}
long srcSize = imageBytes.length;
double accuracy = getAccuracy(srcSize / 1024);
try {
while (imageBytes.length > desFileSize * 1024) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
Thumbnails.of(inputStream).scale(accuracy).outputQuality(accuracy).toOutputStream(outputStream);
imageBytes = outputStream.toByteArray();
}
logger.info("【图片压缩】imageId={} | 图片原大小={}kb | 压缩后大小={}kb", imageId, srcSize / 1024,
imageBytes.length / 1024);
} catch (Exception e) {
logger.error("【图片压缩】msg=图片压缩失败!", e);
}
return imageBytes;
}
/**
* 自动调节精度(经验数值)
*
* @param size 源图片大小
* @return 图片压缩质量比
*/
private static double getAccuracy(long size) {
double accuracy;
if (size < 900) {
accuracy = 0.85;
} else if (size < 2047) {
accuracy = 0.6;
} else if (size < 3275) {
accuracy = 0.44;
} else {
accuracy = 0.4;
}
return accuracy;
}
public static void main(String[] args) throws IOException {
//将文件内容读入字节数组。文件始终处于关闭状态。
byte[] bytes = FileUtils.readFileToByteArray(new File("C:\\Users\\hemanman6\\Desktop\\微信图片_20210802151348.jpg"));
long l = System.currentTimeMillis();
bytes = ThumbnailUtis1.compressPictureForScale(bytes,30,"x");
System.out.println(System.currentTimeMillis()-1);
//将字节数组写入到文件
FileUtils.writeByteArrayToFile(new File("C:\\Users\\hemanman6\\Desktop\\333.jpg"),bytes);
}
}
其中,FileUtils类需要引入如下依赖:
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
结果显示: