一次线上图片打印失败问题排查
01 问题由来
昨天产品在测试系统的时候,提了个bug,有个功能打印图片时,图片无法显示。为了重现这个bug,特意去测试环境看了下,还真存在,于是去看错误日志,发现报异常了。
在某个类的某一行,有这个java.lang.ClassNotFoundException: com.sun.image.codec.jpeg.JPEGCodec
报错信息,于是启动开发环境,打好断点,开始调试。
JPEGCodec
是com.sun.image.codec.jpeg
包下面的,在jdk1.6及以前的版本中都可以正常使用,但是在jdk1.7以上的版本中,就被移除了,但是在jre中的rt.jar包中,还是保留着。
由于本地开发环境配置项目的依赖库时,用的时jre1.8的库,所以在开始调试的时候,一切正常,但是把依赖库切换到Java-SE-1.8
的时候,直接编译报错了。
02 旧的方法
/**
* 对图片进行压缩
* @param filePath 图片文件路径
* @param bufferedImg BufferedImage对象
*/
public static void compressImage(String filePath, BufferedImage bufferedImg) {
FileOutputStream out = null;
try {
if (bufferedImg == null) {
return ;
}
out = new FileOutputStream(filePath);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(bufferedImg);
jep.setQuality(0.8f, true);
encoder.encode(bufferedImg, jep);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
03 使用ImageIO
Image
的getScaledInstance(int width, int height, int hints)
方法中,参数width
是压缩图片的宽,参数height
是压缩图片的高,参数hints
是选择不同的压缩算法,有五种:SCALE_DEFAULT
、SCALE_FAST
、SCALE_SMOOTH
、SCALE_REPLICATE
、SCALE_AREA_AVERAGING
。
width
、height
都为负数时,就使用原始图像尺寸,如果为正数且大小小于原始图片尺寸,就会在原图左上角部分覆盖。
Image.SCALE_SMOOTH
的缩略算法,生成缩略图片的平滑度的优先级比缩放速度高,生成的图片质量比较好,但速度慢。
/**
* 对图片进行压缩
* @param filePath 图片文件路径
* @param bufferedImg BufferedImage对象
*/
public static void compressImage2(String filePath, BufferedImage bufferedImg) {
FileOutputStream out = null;
try {
if (bufferedImg == null) {
return ;
}
bufferedImg.getGraphics().drawImage(bufferedImg.getScaledInstance(-60, -60, bufferedImg.SCALE_SMOOTH), 0, 0, null);
out = new FileOutputStream(filePath);
ImageIO.write(bufferedImg, "jpeg", out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
04 新旧方法测试
编写main方法,找一张图片来测试下。
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
* java.lang.ClassNotFoundException: com.sun.image.codec.jpeg.JPEGCodec
*
* @author 小川
* @date 2019-07-31 20:37:25
*/
public class ImageIO_2019_07_31 {
/**
* 对图片进行压缩
*
* @param filePath 图片文件路径
* @param bufferedImg BufferedImage对象
*/
public static void compressImage(String filePath, BufferedImage bufferedImg) {
FileOutputStream out = null;
try {
if (bufferedImg == null) {
return;
}
out = new FileOutputStream(filePath);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(bufferedImg);
jep.setQuality(0.8f, true);
encoder.encode(bufferedImg, jep);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 对图片进行压缩
*
* @param filePath 图片文件路径
* @param bufferedImg BufferedImage对象
*/
public static void compressImage2(String filePath, BufferedImage bufferedImg) {
FileOutputStream out = null;
try {
if (bufferedImg == null) {
return;
}
/**
* Image.getScaledInstance(int width, int height, int hints)
* width、height都为负数,就使用原始图像尺寸,如果为正数且大小小于原始图片尺寸,就会在原图左上角部分覆盖
* Image.SCALE_SMOOTH的缩略算法,生成缩略图片的平滑度的优先级比缩放速度高,生成的图片质量比较好,但速度慢
*/
bufferedImg.getGraphics().drawImage(bufferedImg.getScaledInstance(-60, -60, bufferedImg.SCALE_SMOOTH), 0, 0, null);
out = new FileOutputStream(filePath);
ImageIO.write(bufferedImg, "jpeg", out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 根据本地文件得到BufferedImage对象
*
* @param filePath 图片文件路径
* @return
*/
public static BufferedImage getBufferedImageByExistFile(File file) {
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
public static void main(String[] args) {
// 原图196kb
String filePath = "E:\\jumao.jpg";
// 创建File对象
File file = new File(filePath);
// 获取BufferedImage对象
BufferedImage bufferedImg = getBufferedImageByExistFile(file);
// 原来的方法 143kb
compressImage("E:\\jumao01.jpg", bufferedImg);
// 调用升级后的方法 107kb
compressImage2("E:\\jumao02.jpg", bufferedImg);
}
}
在E盘中,生成了两张压缩后的图片,从图片的大小上来看,新方法是完胜旧方法的。
如果你也有遇到过此问题,也可以使用上面的新方法,兼容性更好。