java工具类在请看上面的立即下载,也可以复制下面的源码,转载请注明出处,谢谢
压缩效果可观,压缩模式多种,可以单张,可以批量
使用方法看里面ThumbnailUtil.java 代码的注释,具体业务使用步骤看后面
ThumbnailUtil.java 代码
import org.apachemons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.*;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.stream.StreamSupport;public class ThumbnailUtil {/*** 图片压缩,代码来源 bird.thumbnailator包** 例子:** String source = "E:\图片压缩\图片";* String traget = "E:\图片压缩\图片3";* 将目录图片下的所有能压缩的图片使用中等质量压缩到目录图片3,文件名保持不变,输出格式为jpg* ThumbnailUtil.of(new File(source).adFilter())).identifyCompress(ThumbnailUtil.ratios[1])* .outputFormat("jpg").toFiles(new File(traget), null);** 将目录图片下的所有能压缩的图片使用尺寸不变,质量压缩50%压缩到目录图片3,文件名和格式使用原有输入的文件名和格式输出* ThumbnailUtil.of(new File(source).adFilter())).scale(1D).outputQuality(0.5D)* .outputFormat(ThumbnailUtilForm).toFiles(new File(traget), "");** 将图片目录下的原图.jpg使用中等质量压缩到目录图片3,文件名和格式使用原有输入的文件名和格式输出* String source = "E:\图片压缩\图片\原图.jpg";* String traget = "E:\图片压缩\图片3\原图.jpg";* ThumbnailUtil.of(new File(source)).identifyCompress(ThumbnailUtil.ratios[1])* .toFile(new File(traget));** 将图片目录下的原图.jpg使用尺寸不压缩,质量压缩到到目标图片40%质量,文件名和格式使用输入的文件名和格式输出* String source = "E:\图片压缩\图片\原图.jpg";* String traget = "E:\图片压缩\图片3\原图.jpg";* ThumbnailUtil.of(new File(source)).scale(1D).outputQuality(0.4D).toFile(new File(traget));* */// 压缩比率, 低(原质量*0.85),中(原质量*0.7),高(原质量*0.6)public static String[] ratios = new String[]{"low", "medium", "high"};// 原始格式public static String orgForm = "orgForm";public static Builder<File> files) {Iterable<File> iter = Arrays.asList(files);return new Builder<>(iter);}public static Builder<BufferedImage> images) {return new Builder<>(Arrays.asList(images));}public static Builder<InputStream> inputStreams) {return new Builder<>(Arrays.asList(inputStreams));}public static Builder<MultipartFile> multipartFiles) {return new Builder<>(Arrays.asList(multipartFiles));}public static FilenameFilter readFilter() {String readFormats[] = ReaderFormatNames();Set<String> readFormatSet = new HashSet<>(Arrays.asList(readFormats));String writeFormats[] = WriterFormatNames();return new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {String seprator = ".";if (name == null || !ains(seprator)) {return false;}String format = name.substring(name.lastIndexOf(seprator) + 1);ains(format);}};}public static class Builder<T> {// 待转换源数据private final Iterable<T> sources;// 输出格式private String outputFormat = null;// // 原图宽
// private int width = -1;
// // 原图高
// private int height = -1;// 压缩比率private String compressionRatio = null;// 缩放后宽private double scaleWidth = Double.NaN;// 缩放后高private double scaleHeight = Double.NaN;// 压缩质量系数 0-1之间private double outputQuality = Double.NaN;private Builder() {sources = null;}private Builder(Iterable<T> sources) {this.sources = sources;}public Builder<T> identifyCompress(String compressionRatio) {if (!Objects.equals(Double.NaN, scaleWidth)|| !Objects.equals(Double.NaN, scaleHeight)|| !Objects.equals(Double.NaN, outputQuality)) {// 有设置scale和outputQuality则不使用自动压缩选项return this;} else if (null == compressionRatio) {thispressionRatio = ratios[1];return this;}if (!String(ratios).contains(compressionRatio)) {throw new IllegalArgumentException("Unsupported compressionRatio Type.");}thispressionRatio = compressionRatio;return this;}private Builder<T> identifyCompress(String compressionRatio, int width, int height) {if (width <= 0 || height <= 0) {throw new IllegalArgumentException("Width (" + width + ") and height (" + height + ") cannot be <= 0");}// 为了支持多线程压缩, 需要将可变变量直接传入方法中,不能使用共享变量返回scaleWidth和outputQualityif (!Objects.equals(Double.NaN, scaleWidth)|| !Objects.equals(Double.NaN, scaleHeight)|| !Objects.equals(Double.NaN, outputQuality)) {// 有设置scale和outputQuality则不使用自动压缩选项return this;} else if (null == compressionRatio) {compressionRatio = ratios[1];}if (!String(ratios).contains(compressionRatio)) {throw new IllegalArgumentException("Unsupported compressionRatio Type.");}int min = width < height ? width : height;double offset;Builder builder = new Builder();if (Objects.equals(ratios[0], compressionRatio)) {// 最低压缩,图片保持原来尺寸,质量为原来的0.8builder.scaleWidth = builder.scaleHeight = 1.0D;builder.outputQuality = 0.8D;return builder;} else if (Objects.equals(ratios[1], compressionRatio)) {offset = 0.4D;} else {offset = 0.3D;}if (min <= 1024) {// 最小像素小于1024,长和宽不压缩builder.scaleWidth = builder.scaleHeight = 1.0D;builder.outputQuality = (builder.outputQuality = 0.3D + offset) <= 1 ? builder.outputQuality : 1;} else if (min > 1024 && min <= 3 * 1024) {builder.scaleHeight = (builder.scaleHeight = 0.4D + offset) <= 1 ? builder.scaleHeight : 1;builder.scaleWidth = builder.scaleHeight;builder.outputQuality = (builder.outputQuality = 0.3D + offset) <= 1 ? builder.outputQuality : 1;} else {builder.scaleHeight = (builder.scaleHeight = 2048D / min + offset) <= 1 ? builder.scaleHeight : 1;builder.scaleWidth = builder.scaleHeight;builder.outputQuality = builder.scaleHeight;}return builder;}public Builder<T> scale(double scaleWidth, double scaleHeight) {if (scaleWidth <= 0.0 || scaleHeight <= 0.0) {throw new IllegalArgumentException("The scaling factor is equal to or less than 0.");}if (Double.isNaN(scaleWidth) || Double.isNaN(scaleHeight)) {throw new IllegalArgumentException("The scaling factor is not a number.");}if (Double.isInfinite(scaleWidth) || Double.isInfinite(scaleHeight)) {throw new IllegalArgumentException("The scaling factor cannot be infinity.");}this.scaleWidth = scaleWidth;this.scaleHeight = scaleHeight;return this;}public Builder<T> scale(double scale) {return scale(scale, scale);}public Builder<T> outputQuality(double quality) {if (quality < 0.0f || quality > 1.0f) {throw new IllegalArgumentException("The quality setting must be in the range 0.0f and " +"1.0f, inclusive.");}outputQuality = quality;return this;}public Builder<T> outputFormat(String formatName) {if (StringUtils.isEmpty(formatName)) {this.outputFormat = orgForm;return this;} else if (Objects.equals(orgForm, formatName)) {this.outputFormat = formatName;return this;}Iterator<ImageWriter> writers = ImageWritersByFormatName(formatName);if (!writers.hasNext()) {throw new UnsupportedOperationException("No suitable ImageWriter found for " + formatName + ".");}this.outputFormat = formatName;return this;}private String outputFormat(T source, String formatName) throws IOException {if (source == null) {throw new IllegalArgumentException("The resource being processed is null.");}if (StringUtils.isEmpty(formatName)) {formatName = orgForm;} else if (!Objects.equals(orgForm, formatName)) {return formatName;}Iterator<ImageReader> iterReader = ateImageInputStream(source));if (null == iterReader || !iterReader.hasNext()) {throw new UnsupportedOperationException("The resource being processed is not a picture.");}formatName = ().getFormatName();Iterator<ImageWriter> writers = ImageWritersByFormatName(formatName);if (!writers.hasNext()) {throw new UnsupportedOperationException("No suitable ImageWriter found for " + formatName + ".");}return formatName;}private void write(T source, final ImageOutputStream outputStream) throws IOException {// if (StringUtils.isEmpty(outputFormat)) {// throw new IllegalStateException("Output format has not been set.");// }quireNonNull(outputStream, "Could not open OutputStream.");BufferedImage srcImage;if (source instanceof BufferedImage) {srcImage = (BufferedImage) source;} else if (source instanceof File) {srcImage = ad((File) source);} else if (source instanceof MultipartFile) {srcImage = ad(((MultipartFile) source).getInputStream());// 将MultipartFile装换为InputStreamsource = (T) ((MultipartFile) source).getInputStream();} else if (source instanceof InputStream) {srcImage = ad((InputStream) source);} else {throw new IllegalArgumentException("Unsupported ImageIO Type.");}String outputFormatName = this.outputFormat(source, outputFormat);// 原图宽int width = Width();// 原图高int height = Height();// 如果没有设置宽高和压缩比,则自动识别最佳压缩比Builder builder = this.identifyCompress(compressionRatio, width, height);double scaleWidth = builder.scaleWidth;double scaleHeight = builder.scaleHeight;double outputQuality = builder.outputQuality;if (Objects.equals(outputQuality, Double.NaN)) {throw new IllegalArgumentException("outputQuality is null.");}// 缩放后宽int sclWidth = Objects.equals(Double.NaN, scaleWidth) ? width : (int) (width * scaleWidth);// 缩放后高int sclHeight = Objects.equals(Double.NaN, scaleHeight) ? height : (int) (height * scaleHeight);
// Image from = ScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);// 输出BufferedImage流long startTime = System.currentTimeMillis();BufferedImage destImage =new BufferedImage(sclWidth, sclHeight, BufferedImage.TYPE_INT_RGB);Graphics2D g = ateGraphics();// 消除锯齿g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_DEFAULT);g.addRenderingHints(new HashMap<>());g.drawImage(srcImage, 0, 0, sclWidth, sclHeight, null);// 压缩后增加一点点锐化,如不需要的,以下4行代码可以干掉// 拉普拉斯边缘锐化
// startTime = System.currentTimeMillis();
// BufferedImage imageSharpen = ImageSharpen.lapLaceSharpDeal(destImage);
// //设置为透明覆盖
// g.Instance(AlphaComposite.SRC_ATOP, 0.2f));
// //在背景图片上添加锐化的边缘
// g.drawImage(imageSharpen, 0, 0, Width(), Height(), null);
// // 释放对象 透明度设置结束
// g.Instance(AlphaComposite.SRC_OVER));g.dispose();ImageWriter writer = null;ImageTypeSpecifier type ateFromRenderedImage(destImage);// formatName不生效, 所以统一使用jpg
// Iterator iterIO = ImageWriters(type, outputFormatName);Iterator iterIO = ImageWriters(type, "jpg");if (iterIO.hasNext()) {writer = (ImageWriter) ();}if (writer == null) {throw new IllegalArgumentException("ImageWriter is null.");}IIOImage iioImage = new IIOImage(destImage, null, null);ImageWriteParam param = DefaultWriteParam();if (param.canWriteCompressed() && !outputFormatName.equalsIgnoreCase("bmp")) {param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);param.setCompressionQuality((float) outputQuality); //这里可以指定压缩的程度 0-1.0} else {
// param.setCompressionQuality(0.0f);}// ImageOutputStream outputStream = ateImageOutputStream(os);// if (outputStream == null) {
// throw new IOException("Could not open OutputStream.");
// }writer.setOutput(outputStream);writer.write(null, iioImage, param);writer.dispose();outputStream.close();}public ByteArrayInputStream asByteArray() throws IOException {Iterator<T> iter = sources.iterator();T source = ();if (iter.hasNext()) {throw new IllegalArgumentException("Cannot create one thumbnail from multiple original images.");}// 将缓存中的图片按照指定的配置输出到字节数组中ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();write(source, ateImageOutputStream(byteArrayOutputStream));// 从字节数组中读取图片ByteArrayInputStream byteArrayInputStream = new ByteArray());
// InputStream inputStream = new ByteArrayInputStream(byteArrayInputStream);
// MultipartFile file = new MockMultipartFile(ContentType.APPLICATION_String(), byteArrayInputStream);return byteArrayInputStream;}public void toFile(final File outFile) throws IOException {Iterator<T> iter = sources.iterator();T source = ();if (iter.hasNext()) {throw new IllegalArgumentException("Cannot create one thumbnail from multiple original images.");}write(source, ateImageOutputStream(outFile));}private void toFiles(Iterable<File> iterable) throws IOException {Iterator<File> filenameIter = iterable.iterator();for (T source : sources) {if (!filenameIter.hasNext()) {throw new IndexOutOfBoundsException("Not enough file names provided by iterator.");}write(source, ()));}}public void toFiles(File destinationDir, String namePrefix) throws IOException {if (destinationDir == null && namePrefix == null) {throw new NullPointerException("destinationDir and rename is null.");}if (destinationDir != null && !destinationDir.isDirectory()) {destinationDir.mkdir();
// throw new IllegalArgumentException("Given destination is not a directory.");}if (destinationDir != null && !destinationDir.isDirectory()) {throw new IllegalArgumentException("Given destination is not a directory.");}long startTime = System.currentTimeMillis();Builder<T> builder = outputFormat(outputFormat);StreamSupport.stream(sources.spliterator(), true).forEach(source -> {if (!(source instanceof File)) {throw new IllegalStateException("Cannot create thumbnails to files if original images are not from files.");}File f = (File) source;File actualDestDir = destinationDir == null ? f.getParentFile() : destinationDir;String name = StringUtils.isEmpty(namePrefix) ? f.getName() : namePrefix + f.getName();if (!Objects.equals(orgForm, builder.outputFormat)) {name = name.substring(0, name.lastIndexOf(".")) + "." + outputFormat;}File destinationFile = new File(actualDestDir, name);try {write((T) source, ateImageOutputStream(destinationFile));} catch (Exception e) {e.printStackTrace();}});}public void toOutputStream(final OutputStream outputStream) throws IOException {Iterator<T> iter = sources.iterator();T source = ();if (iter.hasNext()) {throw new IllegalArgumentException("Cannot create one thumbnail from multiple original images.");}write(source, ateImageOutputStream(outputStream));}public void toOutputStreams(Iterable<? extends OutputStream> iterable) throws IOException {Iterator<? extends OutputStream> filenameIter = iterable.iterator();for (T source : sources) {if (!filenameIter.hasNext()) {throw new IndexOutOfBoundsException("Not enough file names provided by iterator.");}write(source, ()));}}}
}
我这边使用方法步骤如下,单张图片
// bytes 图片字节数组, targetPath 保存路径
public static Boolean compressImage(byte [] bytes, String targetPath) {try {// 创建一个临时文件File tempFile = ateTempFile("temp", ".jpg");FileOutputStream fos = new FileOutputStream(tempFile);fos.write(bytes);// 获得水印后的文件大小long fileSize = tempFile.length() / 1024;// 判断文件大小,决定压缩倍率,开始压缩图片if (fileSize < 80) { // 图片小于80k,直接上传// 开始上传// 这里自己写.......................return}// 默认压缩率0.9,压缩率越低,压缩强度越高double quality = 0.9;if (fileSize > 150 && fileSize <= 300) { // 150 以上,300k以内的图片,压缩0.8quality = 0.8;} else if (fileSize > 300 && fileSize <= 500) { // 300 以上,500k以内的图片,压缩0.6quality = 0.6;} else if (fileSize > 500 && fileSize <= 1024) { // 500 以上,2M以内的图片,压缩0.4quality = 0.4;} else if (fileSize > 1024 && fileSize <= 5012) { // 1M以上,5M以内的图片,压缩0.3quality = 0.3;} else if (fileSize > 5012) { // 5M以上的图片,压缩0.2quality = 0.2;}double scale = 1;if (fileSize > 150) {// 获取图片宽高决定缩放比例BufferedImage imageIo = ad(new ByteArrayInputStream(bytes));int width = Width();int height = Height();if (width > 1000 && width <= 2000 && height > 1000 && height <= 2000) {scale = 0.8;} else if (width > 2000 && height > 2000) {scale = 0.5;}if (width > 3000 && height > 3000) {scale = 0.3;}}File saveFile = new File(targetPath);// 开始压缩ThumbnailUtil.of(tempFile).scale(scale).outputQuality(quality).toFile(saveFile);// 如果压缩后的文件比压缩前还要大,则删除掉,直接存压缩前的Long saveFileSize = saveFile.length() / 1024;if (saveFileSize > fileSize) {saveFile.delete();// 开始上传// 这里自己写...........................return}// 删除临时文件tempFile.delete();return true;} catch (Exception e) {e.printStackTrace();return false;}}
码字不易,于你有利,勿忘点赞
孤村落日残霞,轻烟老树寒鸦
本文发布于:2024-01-28 10:48:51,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064101366884.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |