要求:
1.代码简单,运行效率高
2.图片不失真(压缩后依然清晰)
3.能压缩到指定kb值(允许尺寸按参数值缩小)
呵呵:一张3M的图片,要求不缩小宽高 、要求不失真!!! 还想压缩到比较小kb值(比如100kb) ;别往下看了。
下文能达到的:
允许按比例缩小宽高,如果达不到100kb,允许图片稍微模糊。
添加依赖:
net.coobird
thumbnailator
0.4.11
代码
package com.solar.oa.card.util;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.coobird.thumbnailator.Thumbnails;
/**
* 图片处理
* @ClassName: ImageUtil.java
* @Description: TODO
* @date 2020年6月30日 下午2:24:55
* @author yqwang
*/
public class ImageUtil {
private final static Logger logger = LoggerFactory.getLogger(ImageUtil.class);
/**
* 【压缩图片】到要求的图片标准: 宽[300,500],高[300,500];100kb;jpg/jpeg
* 先按宽高压缩,压缩后如果还不满足100kb,则按质量递归压缩
* @Title:compressPhoto
* @Description: TODO
* @date 2020年6月30日 下午2:11:52
* @author yqwang
* @param imgFile 源图片文件
* @param maxkb 目标kb
* @param photoWidth 目标宽度
* @param photoHeight 模板高度
* @param quality 质量
* @return 处理后的文件byte[]
* @throws Exception
*/
public static byte[] compressPhoto(File imgFile,Integer maxkb,Integer photoWidth,Integer photoHeight,Float quality) throws Exception{
// 1.压缩图片是否存在
if(imgFile == null || !imgFile.exists()){
throw new Exception("图片文件不存在。");
}
byte[] bytes = readFileToByteArray(imgFile);
if(bytes == null || bytes.length == 0){
throw new Exception("图片文件为空。");
}
// 2.是否超过100kb?没超过就不处理图片
long fileSize = bytes.length;
if (fileSize <= maxkb * 1024) {
logger.info("图片不超过{}kb,无需压缩。",maxkb);
return bytes;
}
// 3.压缩到300-500宽高,100kb
BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes));
int imgWidth = bim.getWidth();
int imgHeight = bim.getHeight();
// 3.1.先按宽高判断是否需要缩放 outputQuality=1确保画质清晰
if(imgWidth >= photoWidth && imgHeight >= photoHeight){
logger.info("先按宽{}高{}压缩。",photoWidth,photoHeight);
ByteArrayOutputStream out = new ByteArrayOutputStream();//Closing a ByteArrayOutputStream has no effect. 因此无需close
Thumbnails.of(new ByteArrayInputStream(bytes)).size(photoWidth, photoHeight).outputQuality(1).outputFormat("jpg").toOutputStream(out);
bytes = out.toByteArray();
}
// 3.2.判断按宽高缩放后是否超过100kb,超过递归就按质量压缩(图片会变得跟模糊!)
bytes = compressPhotoByQuality(bytes, quality, maxkb);
return bytes;
}
/**
* 递归按quality质量处理,压缩到maxkb后返回新的bytes值
* @Title:compressPhotoByQuality
* @Description: TODO
* @date 2020年6月30日 下午2:24:36
* @author yqwang
* @param bytes 源文件字节
* @param quality 压缩质量 (如果为1则不处理)
* @param maxkb 要求压缩到的最大kb
* @return
* @throws IOException
*/
public static byte[] compressPhotoByQuality(byte[] bytes,Float quality,long maxkb) throws IOException{
if(bytes == null){
return bytes;
}
logger.info("开始按质量压缩图片({}kb)。",bytes.length/1024);
// 如果配置的1,则不再处理,多说无益
if(quality == 1){
logger.info("quality=1,不执行压缩。");
return bytes;
}
// 满足目标kb值,则返回
long fileSize = bytes.length;
if (fileSize <= maxkb * 1024) {
logger.info("图片文件{}kb<={}kb,不再压缩质量。",fileSize/1024,maxkb);
return bytes;
}
// Closing a ByteArrayOutputStream has no effect. 因此无需close
ByteArrayOutputStream out = null;
out = new ByteArrayOutputStream();
BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes));
int imgWidth = bim.getWidth();
int imgHeight = bim.getHeight();
// 如果不处理size,只用quality,可能导致一致压缩不到目标值,一致递归在当前方法中!!
int desWidth = new BigDecimal(imgWidth).multiply(new BigDecimal(quality)).intValue();
int desHeight = new BigDecimal(imgHeight).multiply(new BigDecimal(quality)).intValue();
logger.info("图片文将按照width={}*height={}进行压缩,画质quality={}。",desWidth,desHeight,quality);
Thumbnails.of(new ByteArrayInputStream(bytes)).size(desWidth, desHeight).outputQuality(quality).outputFormat("jpg").toOutputStream(out);
//递归
return compressPhotoByQuality(out.toByteArray(), quality, maxkb);
}
/**File to bytes[]*/
public static byte[] readFileToByteArray(File f) throws Exception {
byte[] fileb = null;
InputStream is = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
is = new FileInputStream(f);
byte[] b = new byte[1024];
int n;
while ((n = is.read(b)) != -1) {
out.write(b, 0, n);
}
fileb = out.toByteArray();
return fileb;
} catch (Exception var16) {
throw new Exception("readFileToByteArray", var16);
} finally {
if (is != null) {
try {
is.close();
} catch (Exception var15) {
;
}
}
if (out != null) {
try {
out.close();
} catch (Exception var14) {
;
}
}
}
}
}
测试
package com.solar.oa.card.util;
import java.io.File;
import java.io.FileOutputStream;
import org.apache.commons.codec.binary.Base64;
public class ImageTest {
public static void main(String[] args) throws Exception {
// 图片压缩
File file = new File("D://test/1.jpg");
/*@Parameters:imgFile 源图片文件;maxkb 目标kb;photoWidth 目标宽度;photoHeight 模板高度;quality 质量*/
byte[] bytes = ImageUtil.compressPhoto(file, 100, 300, 300, 0.9f);
// 输出到文件
File desFile = new File("D://test/2.jpg");
FileOutputStream fos = new FileOutputStream(desFile);
fos.write(bytes);
fos.close();
// 转base64
System.out.println("data:image/jpg;base64,"+ Base64.encodeBase64String(bytes));
}
}
效果:1.86MB-->58kb,且压缩后图片依然清晰!
source.jpg: 2708x3085 1.86MB
result.jpg:263x300 58kb
result: