一次线上图片打印失败问题排查

2019-08-09 00:00:00 失败 排查 线上

01 问题由来

昨天产品在测试系统的时候,提了个bug,有个功能打印图片时,图片无法显示。为了重现这个bug,特意去测试环境看了下,还真存在,于是去看错误日志,发现报异常了。

在某个类的某一行,有这个java.lang.ClassNotFoundException: com.sun.image.codec.jpeg.JPEGCodec报错信息,于是启动开发环境,打好断点,开始调试。

JPEGCodeccom.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

ImagegetScaledInstance(int width, int height, int hints)方法中,参数width是压缩图片的宽,参数height是压缩图片的高,参数hints是选择不同的压缩算法,有五种:SCALE_DEFAULTSCALE_FASTSCALE_SMOOTHSCALE_REPLICATESCALE_AREA_AVERAGING

widthheight都为负数时,就使用原始图像尺寸,如果为正数且大小小于原始图片尺寸,就会在原图左上角部分覆盖。

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盘中,生成了两张压缩后的图片,从图片的大小上来看,新方法是完胜旧方法的。
《一次线上图片打印失败问题排查》

如果你也有遇到过此问题,也可以使用上面的新方法,兼容性更好。

相关文章