要求:
1.代码简单,运行效率高
2.图片不失真(压缩后依然清晰)
3.能压缩到指定kb值(允许尺寸按参数值缩小)
呵呵:一张3M的图片,要求不缩小宽高 、要求不失真!!! 还想压缩到比较小kb值(比如100kb) ;别往下看了。
下文能达到的: 允许按比例缩小宽高,如果达不到100kb,允许图片稍微模糊。
添加依赖:
<!-- google图片处理 --> <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.11</version> </dependency>代码
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 <tt>ByteArrayOutputStream</tt> 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 <tt>ByteArrayOutputStream</tt> 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: