作者:丘杨(如需转载请注明出处)
持续更新ing............
背景:
网页中图片和视频资源较多且文件较大,导致网页加载非常慢。图片和视频均存储于minio中,前端会直接访问minio链接
故尝试后端通过压缩图片,前端只展示压缩后的图片来优化加载速度过慢的问题。
其中遇到的一大难题是png动图经过压缩后失去了动图效果。
可能原因:
以下是gpt针对于这个问题可能原因的回答
PNG动图压缩后失去动画效果可能是由于以下原因:
- 压缩算法或参数设置不正确:压缩PNG动图时,如果使用不适当的压缩算法或参数设置,可能会导致图像数据损坏或帧序混乱,从而失去动画效果。
- 压缩软件存在问题:使用的压缩软件可能存在bug或问题,导致在压缩过程中出现错误或异常,从而无法保留动画效果。
- 压缩后文件格式不支持动画:如果压缩后的文件格式不支持动画,例如某些PNG图片查看器不支持GIF格式的动画,那么压缩后的动图可能会失去动画效果。
- 软件缓存问题:某些软件缓存可能会导致动画效果无法正确显示,特别是在多次压缩或转换之后,缓存可能会干扰图像的显示和播放。
为了确保PNG动图压缩后保留动画效果,可以采取以下措施:
- 使用适当的压缩算法和参数:在压缩PNG动图时,选择适当的压缩算法和参数,例如使用优速文件件压缩器等软件工具进行压缩时,可以选择不改变图片分辨率的压缩方式,以确保动画效果得到保留。
- 使用可靠的压缩软件:选择经过测试和验证的可靠压缩软件,确保软件在压缩过程中不会对图像造成损坏或出现异常。
- 检查输出文件格式:在压缩之前,检查输出文件的格式是否支持动画效果,例如GIF格式支持动画,而PNG格式则不支持。如果需要保留动画效果,应该选择支持动画的格式进行输出。
- 清除软件缓存:在使用软件进行压缩之后,可以尝试清除软件缓存,以确保图像的显示和播放不会受到缓存的影响。
失败案例记载:
一、jpedal:PngCompressor.compress(inputStream, outputStream);
maven:
<dependency>
<groupId>org.jpedal</groupId>
<artifactId>OpenViewerFX</artifactId>
<version>6.6.14</version>
</dependency>
代码:
import com.idrsolutions.image.png.PngCompressor;
public void addCompression(String fileName) {
try {
// 读取minio获取文件流并重新上传,更新url readService具体方法见文末
InputStream inputStream = readService.getFileInputStream(bucketName, fileName);
String compressName = "compress_" + fileName;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PngCompressor.compress(inputStream, outputStream);
byte[] byteArray = outputStream.toByteArray();
try { // writeService具体方法见文末
writeService.upload(byteArray, compressName);
try {
outputStream.close();
} catch (Exception ignored) {
}
try {
inputStream.close();
} catch (Exception ignored) {
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
结果:
从1.6mb的文件压缩到9.6kb,但是失去了动画效果
参考文献地址:
Java压缩png图片文件大小,效果跟Tinypng压缩效果大致一样_openviewerfx7.0使用-CSDN博客
二、对图片进行原比例无损压缩,压缩后覆盖原图片
方式1:BufferedImage+Graphics2D
代码:
import java.awt.image.BufferedImage;
public void addCompression(String fileName) {
try {
// 读取minio获取文件流并重新上传,更新url readService具体方法见文末
InputStream inputStream = readService.getFileInputStream(bucketName, fileName);
String compressName = "compress_" + fileName;
// 读取原始动图文件
BufferedImage img = ImageIO.read(inputStream);
int newWidth = img.getWidth();
int newHeight = img.getHeight();
// 创建目标尺寸的缓冲图像
BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
// 获取绘图对象并在目标图像上绘制原始图像
Graphics2D graphics = img.createGraphics();
graphics.drawImage(img, 0, 0, newWidth, newHeight, null);
graphics.dispose();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(tag, "png", outputStream);
writeService.upload(outputStream.toByteArray(), compressName);
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
结果:
压缩后的图像只剩下120b,且打开为
导致这个问题的原因暂未找到
参考文献地址:
java动图压缩_mob649e815cb099的技术博客_51CTO博客
方式2:BufferedImage
代码:
import java.awt.image.BufferedImage;
public void addCompression(String fileName) {
try {
// 读取minio获取文件流并重新上传,更新url readService具体方法见文末
InputStream inputStream = readService.getFileInputStream(bucketName, fileName);
String compressName = "compress_" + fileName;
// 读取原始动图文件
BufferedImage img = ImageIO.read(inputStream);
int newWidth = img.getWidth();
int newHeight = img.getHeight();
// 创建目标尺寸的缓冲图像
BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
tag.getGraphics().drawImage(image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
ImageIO.write(tag, "png", outputStream);
writeService.upload(outputStream.toByteArray(), compressName);
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
结果:
从1.6mb压缩到19.9kb,图片可正常显示但无动画效果
参考文献地址:
三、ImageWriter压缩后生成字节流
方式1:
代码:
public void addCompression(String fileName) {
try {
// 读取minio获取文件流并重新上传,更新url readService具体方法见文末
InputStream inputStream = readService.getFileInputStream(bucketName, fileName);
String compressName = "compress_" + fileName;
// 读取原始动图文件
BufferedImage image = ImageIO.read(inputStream);
// 得到指定Format图片的writer(迭代器)
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("png");
// 得到writer
ImageWriter writer = (ImageWriter) iter.next();
// 得到指定writer的输出参数设置(ImageWriteParam )
ImageWriteParam iwp = writer.getDefaultWriteParam();
// 设置可否压缩
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// 设置压缩质量参数
float quality = 0.5f;
iwp.setCompressionQuality(quality);
iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
ColorModel colorModel = ColorModel.getRGBdefault();
// 指定压缩时使用的色彩模式
iwp.setDestinationType(
new javax.imageio.ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
// 开始打包图片,写入byte[]
// 取得内存输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IIOImage iIamge = new IIOImage(image, null, null);
// 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput
// 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput
writer.setOutput(ImageIO.createImageOutputStream(byteArrayOutputStream));
writer.write(null, iIamge, iwp);
byte[] imgBytes = byteArrayOutputStream.toByteArray();
writeService.upload(imgBytes, compressName);
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
结果:
png图像从1.6mb压缩至27.7kb,但失去动图效果
参考文献地址:
方式2:
由于方式1失去动图效果,基于gpt给的方法进行优化,其中增加了IIOMetadata
代码:
public void addCompression(String fileName) {
try {
// 读取minio获取文件流并重新上传,更新url readService具体方法见文末
InputStream inputStream = readService.getFileInputStream(bucketName, fileName);
String compressName = "compress_" + fileName;
// 读取原始动图文件
BufferedImage image = ImageIO.read(inputStream);
// 得到指定Format图片的writer(迭代器)
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("png");
// 得到writer
ImageWriter writer = (ImageWriter) iter.next();
// 得到指定writer的输出参数设置(ImageWriteParam )
ImageWriteParam iwp = writer.getDefaultWriteParam();
// 设置可否压缩
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// 设置压缩质量参数
float quality = 0.5f;
iwp.setCompressionQuality(quality);
iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
ColorModel colorModel = ColorModel.getRGBdefault();
// 指定压缩时使用的色彩模式
iwp.setDestinationType(
new javax.imageio.ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
// 开始打包图片,写入byte[]
// 取得内存输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IIOImage iIamge = new IIOImage(image, null, null);
// 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput
// 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput
writer.setOutput(ImageIO.createImageOutputStream(byteArrayOutputStream));
writer.write(null, iIamge, iwp);
byte[] imgBytes = byteArrayOutputStream.toByteArray();
writeService.upload(imgBytes, compressName);
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
结果:
文件从1.6mb压缩到56.5kb,png依旧失去动画效果