JPG&PNG图片压缩java实现

最近项目中有一个需求是关于图片处理的,其实关于图片处理java的BufferedImage类基本上已经可以处理绝大多数需求,但是关于png图片的压缩遇到一点小的阻碍,我们知道png图片与JPG图片最大的区别就是可以保存为透明背景的图片,JPEG就不可以,jpeg是一种有损压缩的图片格式。png是可以转换成JPEG的,但是透明像素会变成白色的,而且图像信息会丢失一部分。png图片压缩起来比较困难。下面给出已经在项目中使用的静态方法实现这些需求。

1、图片文件生成BufferedImage对象:

    /**
     * @param file 图片
     * @return boolean true:符合要求
     * @description 图片文件转化为BufferedImage
     */
    public static BufferedImage fileToBufferedImage(File file) throws IOException {
        if (!file.exists()) {
            return null;
        }
        BufferedImage bufferedImage = ImageIO.read(file);
        return bufferedImage;
    }


    /**
     * @param fileByteArray
     * @return BufferedImage
     * @throws IOException
     * @description 图片文件字节数组转化为BufferedImage
     */
    public static BufferedImage fileToBufferedImage(byte[] fileByteArray) throws IOException {
        if (fileByteArray == null || fileByteArray.length == 0) {
            return null;
        }
        ByteArrayInputStream stream = new ByteArrayInputStream(fileByteArray);
        BufferedImage bufferedImage = ImageIO.read(stream);
        return bufferedImage;
    }

2、BufferedImage对象生成图片文件:有两个写法方式:1-简单方式,2-支持高质量压缩的方式

    /**
     * bufferedImage 转化为文件-简单方式
     * @param bufferedImage
     * @param destFile      获取目标路径
     * @param ext           图片的格式:jpg/png
     * @throws IOException
     */
    public static void bufferedImageToFile1(BufferedImage bufferedImage, String destFile, String ext) throws IOException {
        //使用ImageIO的方法进行输出,记得关闭资源
        OutputStream out = new FileOutputStream(destFile);
        ImageIO.write(bufferedImage, ext, out);
        out.close();
    }

    /**
     * bufferedImage 转化为文件-高质量支持压缩的方式
     * @param bufferedImage
     * @param destFile      获取目标路径
     * @param ext           图片的格式:jpg/png
     * @throws IOException
     */
    public static void bufferedImageToFile2(BufferedImage bufferedImage, String destFile, String ext, float quality) throws IOException {
        Iterator<ImageWriter> imageWriterIter = ImageIO.getImageWritersByFormatName(ext);
        if (imageWriterIter.hasNext()) {
            ImageWriter writer = imageWriterIter.next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);    //质量0-1.0
            File file = new File(destFile);
            FileImageOutputStream out = new FileImageOutputStream(file);
            writer.setOutput(out);
            writer.write(null, new IIOImage(bufferedImage, null, null), param);
            out.close();
            writer.dispose();
        }

    }

3、上面的压缩对于jpg和png图片,因为使用高质量的像素,会导致图片精度很高,图片比较大,现在提供对于jpg和png的图片的压缩代码

      对于jpg图片,我们不断降低压缩质量会较大幅度的降低图片大小,因为jpg图片是有损压缩,质量会降低的比较大,参考代码如下:

    /**
     * bufferedImage 目标将图片压缩到500KB以下,最多压缩5次
     * @param bufferedImage
     * @param ext       图片的格式
     * @param quality   默认1.0            
     * @throws IOException
     */
    public static byte[] bufferedImageToFileByte2Less500K(String tempFilePath, BufferedImage bufferedImage, String ext, float quality) throws IOException {
        Iterator<ImageWriter> imageWriterIter = ImageIO.getImageWritersByFormatName(ext);
        if (imageWriterIter.hasNext()) {
            ImageWriter writer = imageWriterIter.next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            System.out.println(quality);
            param.setCompressionQuality(quality);    //最高质量
            File file = new File(tempFilePath+ "/" + new Random().nextInt(10)  + "_temp_heap_image_" + quality + "."+ext);
            FileImageOutputStream out = new FileImageOutputStream(file);
            writer.setOutput(out);
            writer.write(null, new IIOImage(bufferedImage, null, null), param);
            ByteArrayOutputStream swapStream = getByteArrayOutputStream(file);
            out.close();
            writer.dispose();
            if(quality <=0.5f || swapStream.toByteArray().length/1024 <=500){
                return swapStream.toByteArray();
            }else{
                quality = quality - 0.1f;
                return bufferedImageToFileByte2Less500K(tempFilePath,bufferedImage,ext,quality);
            }
        }
        return null;
    }

         上面的代码对于png图片,压缩的很有限,因为png是高质量图片,因此,我们要重新想办法,后来找到一个支持png压缩的工具包:OpenViewerFX,可以将png图片压缩的比较小,而且不怎么失真,代码如下:

        需要引入依赖:

    <dependency>
      <groupId>org.jpedal</groupId>
      <artifactId>OpenViewerFX</artifactId>
      <version>6.6.14</version>
    </dependency>
   /**
     * bufferedImage 转化为文件
     * @param bufferedImage
     * @param ext 图片的格式:png
     * @throws IOException
     */
    public static byte[] bufferedImageToFileByte(String tempFilePath, BufferedImage bufferedImage, String ext) throws IOException {
        //使用ImageIO的方法进行输出,记得关闭资源
        File file = new File(tempFilePath+ "/" + new Random().nextInt(10) + "_temp_heap_image."+ext);
        OutputStream out = new FileOutputStream(file);
        ImageIO.write(bufferedImage, ext, out);
        out.close();
        ByteArrayOutputStream swapStream = getByteArrayOutputStream(file);
        //对于图片是jpg或者小于500KB,就直接输出
        if(ext.equalsIgnoreCase("jpg") || swapStream.toByteArray().length/1024 <=500){
            return swapStream.toByteArray();
        }
        File compressOutFile=new File(tempFilePath+ "/" + new Random().nextInt(10) + "_temp_heap_image_cp."+ext);
        //如果图片是png,且大于500KB,则进行压缩
        PngCompressor.compress(file,compressOutFile);
        swapStream = getByteArrayOutputStream(compressOutFile);
        return swapStream.toByteArray();
    }

下面再提供一些工具类:

    /**
     * @param file        图片
     * @param imageWidth  宽
     * @param imageHeight 高
     * @return boolean true:符合要求
     * @description 校验图片比例
     */
    public static boolean checkImageScale(File file, int imageWidth, int imageHeight) throws IOException {

        Boolean result = false;
        if (!file.exists()) {
            return false;
        }
        BufferedImage bufferedImage = ImageIO.read(file);
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        if (imageHeight != 0 && height != 0) {
            int scale1 = imageHeight / imageWidth;
            int scale2 = height / width;
            if (scale1 == scale2) {
                result = true;
            }
        }
        return result;
    }


    /**
     * @param path 图片路径
     * @return java.lang.String base64字符串
     * @description 将图片文件转化为字节数组字符串,并对其进行Base64编码处理
     */
    public static String imgToBase64Str(String path) throws IOException {
        byte[] data = null;
        // 读取图片字节数组
        InputStream in = null;
        try {
            in = new FileInputStream(path);
            data = new byte[in.available()];
            in.read(data);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //返回Base64编码过的字节数组字符串
        return Base64.encodeBase64String(data);
    }


    /**
     * 按比例对图片进行缩放
     * @param scale 图片根据比例缩放
     * @throws IOException
     */
    public static BufferedImage zoomByScale(BufferedImage bufferedImage, double scale) throws IOException {

        //获取图片的长和宽
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();

        //获取缩放后的长和宽
        int _width = (int) (scale * width);
        int _height = (int) (scale * height);

        //获取缩放后的Image对象
        Image _img = bufferedImage.getScaledInstance(_width, _height, Image.SCALE_DEFAULT);

        //新建一个和Image对象相同大小的画布:BufferedImage.TYPE_INT_ARGB
        BufferedImage image = new BufferedImage(_width, _height, BufferedImage.TYPE_INT_ARGB);
        //获取画笔
        Graphics2D graphics = image.createGraphics();
        //将Image对象画在画布上,最后一个参数,ImageObserver:接收有关 Image 信息通知的异步更新接口,没用到直接传空
        graphics.drawImage(_img, 0, 0, null);
        //释放资源
        graphics.dispose();

        return image;

    }

    /**
     * png -支持图片背景透明
     * 图片指定长和宽对图片进行缩放
     * @param width  长
     * @param height 宽
     * @throws IOException
     */
    public static BufferedImage zoomBySize(BufferedImage bufferedImage, int width, int height) {
        //与按比例缩放的不同只在于,不需要获取新的长和宽,其余相同.
        Image _img = bufferedImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = image.createGraphics();
        graphics.drawImage(_img, 0, 0, null);
        graphics.dispose();
        return image;
    }

    /**
     * @param background 背景
     * @param foreground 前景
     * @param x 前景x坐标
     * @param y 前景y坐标
     * @return
     * @description 将前景图片合成到背景图上
     */
    public static BufferedImage compose(BufferedImage background, BufferedImage foreground, int x, int y) {
        try {
            Graphics2D g = background.createGraphics();
            g.drawImage(foreground, x, y, foreground.getWidth(), foreground.getHeight(), null);
            g.dispose();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return background;

    }


    /**
     * 裁剪png图片的非透明区域,使用最大x和y的边框
     * @param bufferedImage
     * @return
     * @throws IOException
     */
    public static BufferedImage getNotTransparentArea(BufferedImage bufferedImage) throws IOException {

        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int x = 0, y = 0, width = 0, height = 0;
        for (int j = 0; j < h; j++) {
            for (int i = 0; i < w; i++) {
                int dip = bufferedImage.getRGB(i, j);
                if ((dip & 0xff000000) != 0) {
                    if (x == 0 || i < x) {
                        x = i;
                    }
                    if (y == 0 || j < y) {
                        y = j;
                    }
                    if (width == 0 || i > width) {
                        width = i;
                    }
                    if (height == 0 || j > height) {
                        height = j;
                    }
                }
            }
        }
        bufferedImage = bufferedImage.getSubimage(x - 1, y - 1, width - x + 1, height - y + 1);
        return bufferedImage;
    }

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值