最近遇见个需求,需要压缩图片,但是很多涉及到的方法类,要么对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;
}
}