java实现图片分辨率压缩、图片软化、jpg质量压缩


最近遇见个需求,需要压缩图片,但是很多涉及到的方法类,要么对jdk依赖比较大,要么对系统的配置有要求,经过几天的研究和测试,终于整了一个还算比较全的方案。


代码

注解1:关于根据url读取图片的方法有很多,这里解释下为什么使用new ImageIcon(new URL(url)).getImage()而不使用ImageIo.read(new URL(url)),因为ImageIo.read这个方法读取颜色空间为CMKY的图片时会报错bad sequence number

注解2:关于图片质量压缩,网上大多数方法是使用JPEGImageEncoder工具类,但是为什么我们选择ImageWriteParam呢?
JPEGImageEncoder在jdk1.7以后就被移除了,并且还需要使用jre底层的内容。

package com.common.file.img;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;


/**
 * @author zhao
 * @date 2020/8/31 16:11
 */
public class ImageIoUtil {

    private static Logger logger = LoggerFactory.getLogger(ImageIoUtil.class);

    private final static String jpg = "jpg";
    private final static String jpeg = "jpeg";


    public static void main(String[] args) {
        String url = "http://pics.sc.chinaz.com/files/pic/pic9/201509/apic14546.jpg";
        String suffix = url .substring(url.lastIndexOf(".") + 1);
        compression(url,1.5,0.1f,0.5f,suffix);
    }

    /***
     * TODO
     * @author zhao
     * @date 2020/9/3 10:19
     * @param url 图片地址
     * @param num  分辨率缩放比例,取值范围大于1
     * @param quality  图片质量缩放比例,取值范围大于0小于1
     * @param softenFactor  图片软化比例,取值范围大于0,小于1,小于0.1时,图片大小增加
     * @param suffix 图片后缀名
     * @return byte[]
     */
    public static byte[] compression(String url, double num, float softenFactor,
                                     float quality, String suffix) {
        try {

            boolean numFlag = !"".equals(num) && num > 1;
            boolean softenFactorFlag = !"".equals(softenFactor)
                    && softenFactor > 0 && softenFactor < 1;
            boolean qualityFlag = !"".equals(quality)
                    && quality > 0 && quality < 1;
            //判断是否是jpeg或者jpg图片,其他类型不支持质量压缩,至少我没找到方法
            boolean suffixFlag = jpeg.equals(suffix) || jpg.equals(suffix);

            // 描述请见 <注解1>
            Image temp = new ImageIcon(new URL(url)).getImage();

            int w = temp.getWidth(null);
            int h = temp.getHeight(null);

            if (numFlag) {
                //分辨率压缩的后的大小
                h = new Double(h / num).intValue();
                w = new Double(w / num).intValue();
            }

            // 分辨率压缩
            BufferedImage bimage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

            // 将图像复制到缓冲的图像。
            Graphics g = bimage.createGraphics();
            g.drawImage(temp, 0, 0, w, h, Color.LIGHT_GRAY, null);
            g.dispose();

            //如果三个值都不符合取值范围,则不进行压缩
            //其实ImageIo在write时是对质量有影响的。
            if (!numFlag && !softenFactorFlag && !qualityFlag) {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ImageIO.write(bimage, suffix, out);
                return out.toByteArray();
            }

            // 软化
            if (softenFactorFlag) {
                float[] softenArray = {0, softenFactor, 0, softenFactor,
                        1 - (softenFactor * 4), softenFactor, 0, softenFactor, 0};
                Kernel kernel = new Kernel(3, 3, softenArray);
                ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
                bimage = cOp.filter(bimage, null);
            }

            if (suffixFlag && qualityFlag) {
                //质量压缩
                return quality(bimage, quality, suffix);
            } else {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ImageIO.write(bimage, suffix, out);
                return out.toByteArray();
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     * TODO 根据质量压缩图片,为什么选择这个方法请见<注解2>
     * @author zhaosongbin
     * @date 2020/9/3 16:51
     * @param image
     * @param quality
     * @param suffix
     * @return byte[]
     */
    private static byte[] quality(BufferedImage image, float quality, String suffix) {
        try {
            // 得到指定Format图片的writer,得到迭代器
            Iterator iter = ImageIO
                    .getImageWritersByFormatName(suffix);
            ImageWriter writer = (ImageWriter) iter.next();

            // 得到指定writer的输出参数设置(ImageWriteParam )
            ImageWriteParam iwp = writer.getDefaultWriteParam();
            // 设置可否压缩
            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            // 设置压缩质量参数
            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 out = new ByteArrayOutputStream();
            IIOImage iIamge = new IIOImage(image, null, null);

            // 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput
            // 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput
            writer.setOutput(ImageIO
                    .createImageOutputStream(out));
            writer.write(null, iIamge, iwp);
              //根据字节流生成图片和原图做下比较,测试完注释
            FileOutputStream fos = 
                    new FileOutputStream("C:/Users/zhao/Desktop/test/test.jpg");
            fos.write(out.toByteArray());
            fos.close();
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值