Java实现将图片压缩到指定大小
public static File compressImageToSize(MultipartFile multipartFile, double targetSizeMB, String fileName) throws IOException {
//读取原始图片
//获取InputStream字节输入流,通过输入流可读取文件内容。
InputStream inputStream = multipartFile.getInputStream();
//从InputStream中读取图片数据,并将其转换为一个BufferedImage对象(包含图像的像素、宽度和高度等信息。通过这个对象,可以对图像进行缩放、旋转和裁剪等操作)
BufferedImage originalImage = ImageIO.read(inputStream);
//从文件名中提取扩展名
String extension = fileName.contains(".") ? fileName.substring(fileName.lastIndexOf(".")) : ".jpg";
//获取原始图片宽度
int originalWidth = originalImage.getWidth();
//获取原始图片高度
int originalHeight = originalImage.getHeight();
/* 计算缩放因子
* originalWidth * originalHeight / 1000000.0:计算原始图片的面积(以平方米为单位),1000000.0:将像素转换为兆字节所需的因子,因为1MB = 1000000字节,而每个像素通常占用1字节(假设没有压缩)
* targetSizeMB / (originalWidth * originalHeight / 1000000.0):计算目标大小与原始大小的比值
* Math.sqrt(targetSizeMB / (originalWidth * originalHeight / 1000000.0)):对上述比值取平方根,得到缩放因子。
* 文件大小 = 分辨率(宽度像素数 × 高度像素数)× 颜色深度(位深度)/ 8
* eg.
* 假设缩放因子为k,那么缩放后的图像宽度为originalWidth * k,高度为originalHeight * k。
* 缩放后的图像面积为(originalWidth * k) * (originalHeight * k) = originalWidth * originalHeight * k^2。
* 我们希望缩放后的图像面积接近目标大小,即originalWidth * originalHeight * k^2 ≈ targetSizeMB * 1000000。
* 将上述等式两边同时除以originalWidth * originalHeight,得到k^2 ≈ targetSizeMB * 1000000 / (originalWidth * originalHeight)。
* 对两边同时开平方,得到k ≈ sqrt(targetSizeMB * 1000000 / (originalWidth * originalHeight))。
* */
double scaleFactor = Math.sqrt(targetSizeMB / (originalWidth * originalHeight / 1000000.0));
//计算新的图片尺寸
int newWidth = (int) (originalWidth * scaleFactor);
int newHeight = (int) (originalHeight * scaleFactor);
//使用imgscalr进行缩放
/* 使用imgscalr库进行图像缩放,Scalr.resize()方法接受以下参数:
* originalImage:要缩放的原始图像。
* Scalr.Method.QUALITY:缩放算法,这里选择QUALITY,表示优先保证图像质量。
* Scalr.Mode.AUTOMATIC:自动选择最佳的缩放模式。
* newWidth和newHeight:缩放后的图像尺寸。
* Scalr.OP_ANTIALIAS:抗锯齿选项,用于平滑图像边缘。
* */
BufferedImage scaledImage = Scalr.resize(originalImage, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, newWidth, newHeight, Scalr.OP_ANTIALIAS);
//创建一个临时文件,使用原始文件名(不包括扩展名)和扩展名
File compressedFile = new File(fileName);
//JVM退出时,这个文件将被删除。
compressedFile.deleteOnExit();
//写入文件
//OutputStream类型的变量os是一个用于存储输出流对象的引用,通过这个引用,可以调用输出流的方法来执行写操作,例如将数据写入文件或发送数据到网络。
OutputStream os = null;
try{
//创建一个FileOutputStream实例,用于向文件compressedFile写入数据;FileOutputStream实例被赋值变量os,os变量现在持有了一个指向compressedFile文件的输出流,可以通过这个流来写入数据。
os = new FileOutputStream(compressedFile);
//将scaledImage(BufferedImage对象)以extension.substring(1)(去掉扩展名前的点)格式写入到os指定的输出流中。这通常用于将处理后的图像保存到文件中写入到os(OutputStream对象,表示图像数据将被写入的目标位置)中。
ImageIO.write(scaledImage, extension.substring(1), os);
} catch (IOException e) {
throw new IOException("Failed to write the image to file.", e);
}finally {
//关闭输入流(inputStream)和输出流(os),若不关闭,会出现JVM退出时临时文件删除不了的问题
if(os != null){
try{
//关闭multipartFile对应的inputStream输入流
inputStream.close();
}catch (IOException e) {
log.info(e.getMessage());
}finally {
try {
//关闭compressedFile对应的OutputStream输出流
os.close();
}catch (IOException e) {
log.info(e.getMessage());
}
}
}
}
return compressedFile;
}