通过搜索,选定3种方案参考: thumbnailator,OpenCV,ImageIO(java原生)
OpenCV
先选用了OpenCV,引入了maven包后启动报错
java.lang.UnsatisfiedLinkError: org.opencv.imgcodecs.Imgcodecs.imread_1
晚上搜索了下该错误,根据搜索结果添加以下代码段
System.load(Core.NATIVE_LIBRARY_NAME);
再次启动又出现以下报错
java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library
根据搜索发现这个方案比较麻烦,坑比较多,换用javacv能解决依赖的报错问题,但是依赖项太多,感觉比较重,放弃了这个方案
thumbnailator
看maven仓库发现近一年没有更新版本,担心后续漏洞修复或其他支持问题,放弃这个方案

ImageIO
没有直接选用这种方案是因为看到网上有评论说有可能导致内存溢出(作者未复现),本身压缩需求就是因为要处理内存不足问题,所以对这个方案有疑惑。还有评论说压缩会导致某些图片损坏(不确定是工具问题还是设置问题,作者未复现),所以没有作为第一选择。但是在完成代码编写后此方案确实实现了压缩功能,而且测试阶段未发现上述问题。
第一种方法: 保持纵横比,但是缩减尺寸,生成文件跟原图尺寸不一致,但是细节比较好
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\admin\\Desktop\\5d83e1591902458c8cfb1d2ed181c3f0.jpg");
byte[] bytes = compressToJpgBySize(file, 1l * 1024 * 1024);
File outFile = new File("C:\\Users\\admin\\Desktop\\out.jpg");
OutputStream output = new FileOutputStream(outFile);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
bufferedOutput.write(bytes);
bufferedOutput.close();
output.close();
}
private static byte[] compressToJpgBySize(File origin, Long picCompressThreshold) throws IOException {
InputStream input = new FileInputStream(origin);
byte[] bytes = input.readAllBytes();
byte[] fileBytes = bytes;
if(bytes.length > picCompressThreshold){
BufferedImage originalImage = ImageIO.read(origin);
int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
double originalWidth = originalImage.getWidth();
double originalHeight = originalImage.getHeight();
BigDecimal aspectRatio = BigDecimal.valueOf(originalWidth).divide(BigDecimal.valueOf(originalHeight), 4, RoundingMode.HALF_UP);
// 尽量的去靠近阈值大小,比例缩小后文件大小不固定,猜测是色彩原因,所以多压缩一点比例
BigDecimal scale = BigDecimal.valueOf(picCompressThreshold).multiply(BigDecimal.valueOf(0.75)).divide(BigDecimal.valueOf(bytes.length),2,RoundingMode.DOWN).sqrt(MathContext.DECIMAL128).setScale(2,RoundingMode.DOWN);
double newWidth = BigDecimal.valueOf(originalWidth).multiply(scale).setScale(2,RoundingMode.DOWN).doubleValue();
double newHeight = BigDecimal.valueOf(newWidth).divide(aspectRatio,4,RoundingMode.HALF_UP).doubleValue();
// 创建一个新的BufferedImage,类型为原始图片的类型
BufferedImage resizedImage = new BufferedImage((int) newWidth, (int) newHeight, type);
// 使用Graphics2D来绘制缩小后的图片
Graphics2D g2d = resizedImage.createGraphics();
g2d.drawImage(originalImage, 0, 0, (int) newWidth, (int) newHeight, null);
g2d.dispose();
// 将缩小后的图片写入到ByteArrayOutputStream中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "jpg", baos);
fileBytes = baos.toByteArray();
baos.close();
}
input.close();
return fileBytes;
}
第二种: 保持原图尺寸,生成文件跟原图尺寸一致,但是细节放大看很差
压缩质量设置为0.1时,4.45MB图片压缩至1.04MB,这个压缩后图片大小可控性更差
public static void main(String[] args) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
File file = new File("C:\\Users\\admin\\Desktop\\5d83e1591902458c8cfb1d2ed181c3f0.jpg");
System.out.println("图片处理开始"+sdf.format(new Date()));
byte[] bytes = compressToJpgBySize2(file,1l * 1024 * 1024);
// byte[] bytes = compressToJpgBySize(file, 1l * 1024 * 1024);
System.out.println("图片处理结束"+sdf.format(new Date()));
File outFile = new File("C:\\Users\\admin\\Desktop\\out.jpg");
OutputStream output = new FileOutputStream(outFile);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
bufferedOutput.write(bytes);
bufferedOutput.close();
output.close();
}
private static byte[] compressToJpgBySize2(File origin, Long picCompressThreshold) throws IOException {
InputStream input = new FileInputStream(origin);
byte[] bytes = input.readAllBytes();
byte[] fileBytes = bytes;
if(bytes.length > picCompressThreshold){
BufferedImage originalImage = ImageIO.read(origin);
String formatName = origin.getName().substring(origin.getName().lastIndexOf(".") + 1);
ImageWriter writer = ImageIO.getImageWritersByFormatName(formatName).next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.1f); // 设置压缩质量,范围为0-1,数值越小压缩率越高
File file = new File("C:\\Users\\admin\\Desktop\\temp.jpg");
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
writer.write(null, new IIOImage(originalImage, null, null), param);
InputStream res = new FileInputStream(file);
fileBytes = res.readAllBytes();
res.close();
file.deleteOnExit();
}
input.close();
return fileBytes;
}
后续补充
使用imageIo缩减尺寸压缩一张4.45MB图片到1MB以内在服务器上执行了近3S,效率非常差,图片的压缩是 计算密集型操作(查看本地cpu在图片压缩时飙升到100%),业务需求对实时性没有要求,而且需压缩的图片也并不多,于是将压缩图片任务由定时任务异步操作。

3768

被折叠的 条评论
为什么被折叠?



