天天看點

Java圖檔壓縮大小,圖檔縮放

添加依賴

<!-- 圖檔縮略圖 -->
            <dependency>
                <groupId>net.coobird</groupId>
                <artifactId>thumbnailator</artifactId>
                <version>0.4.8</version>
            </dependency>
           

Java壓縮圖檔案例1

部分字尾圖檔會出現壓縮失敗

//壓縮至範圍(50 壓縮到50KB)
    @Value(value = "${jeecg.path.image-comprssion-to}")
    private Integer imageComprssionTo;

    //上傳檔案路徑(win D://opt//upFiles Linux /tang/upload)
    @Value(value = "${jeecg.path.upload}")
    private String uploadpath;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public AiFileStorage uploadZip(String fileName) {
        String originalFilename = "";//檔案名稱
        String filePatha = uploadpath;
        String filePathaBackup = uploadpath;
        AiFileStorage aiFileStorage = new AiFileStorage();
        try {
            File addFile = new File(filePatha);
            if (!addFile.exists()) {
                addFile.mkdirs();
            }
            String findFfilePatha = filePatha + "/" + fileName;
            File file = new File(findFfilePatha);
            File savefile;
            //伺服器上相同名稱的檔案,後面拼接 _compress + i
            int i = -1;
            do {
                i++;
                savefile = new File(filePatha + "/" + fileName.substring(0, fileName.lastIndexOf(".")) + "_compress" + (i == 0 ? "" : i)+ fileName.substring(fileName.indexOf(".")));
                originalFilename = fileName.substring(0, fileName.lastIndexOf(".")) + "_compress" + (i == 0 ? "" : i) + fileName.substring(fileName.indexOf("."));
            } while (savefile.exists());


            filePatha += "/" + originalFilename;//絕對路徑
            filePathaBackup += "/" + fileName;//源檔案
            log.info("位址:" + filePatha);
            log.info("檔案大小:" + new BigDecimal((double)file.length()/1024/1024).divide(new BigDecimal(1),2,BigDecimal.ROUND_HALF_UP)+"M");
            log.info("檔案名" + originalFilename);

            BigDecimal a = new BigDecimal((double)file.length()/1024/1024).divide(new BigDecimal(1),2,BigDecimal.ROUND_HALF_UP);
            Integer productCode = imageComprssionTo;//圖檔壓縮範圍 超出的壓縮
            BigDecimal b = new BigDecimal(StringUtils.getM(productCode));
            File file_b = new File(filePatha);
            if(a.compareTo(b) == 1){//a大于b,壓縮圖檔 a 源檔案大小 b 壓縮至大小
                //壓縮圖檔
                File file_c = new File(filePathaBackup);
                Files.copy(file.toPath(),file_b.toPath());
                Files.copy(file.toPath(),file_c.toPath());
                ImageTool.compress("file:" + filePathaBackup,productCode,file_b.getAbsolutePath());
            }else{
                Files.copy(file.toPath(),file_b.toPath());
            }

            aiFileStorage.setCreateTime(new Timestamp(System.currentTimeMillis()));
            aiFileStorage.setSize(file_b.length()/ 1024+" KB");
            aiFileStorage.setType(2);
            aiFileStorage.setSuffix(FileUtil.getExtensionName(file_b.getName()));
            aiFileStorage.setName(file_b.getName());
            aiFileStorage.setUrl(originalFilename);
            aiFileStorageService.save(aiFileStorage);
            return aiFileStorage;
        } catch (NoSuchFileException e) {
            e.printStackTrace();
            throw new BadRequestException("檔案不存在");
        }  catch (IOException e) {
            e.printStackTrace();
        }
        return new AiFileStorage();
    }
           

檔案記錄實體類

package org.jeecg.modules.ai.entity;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * @Description: 檔案存儲
 * @Date:   2021-01-13
 * @Version: V1.0
 */
@Data
@TableName("ai_file_storage")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="ai_file_storage對象", description="檔案存儲")
public class AiFileStorage implements Serializable {
    private static final long serialVersionUID = 1L;

	/**主鍵*/
	@TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "主鍵")
    private String id;
	/**建立人*/
    @ApiModelProperty(value = "建立人")
    private String createBy;
	/**上傳或同步的時間*/
	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "上傳或同步的時間")
    private Date createTime;
	/**檔案名稱*/
	@Excel(name = "檔案名稱", width = 15)
    @ApiModelProperty(value = "檔案名稱")
    private String name;
	/**檔案大小*/
	@Excel(name = "檔案大小", width = 15)
    @ApiModelProperty(value = "檔案大小")
    private String size;
	/**檔案類型:私有或公開(1,私 2.共)*/
	@Excel(name = "檔案類型:私有或公開(1,私 2.共)", width = 15)
    @ApiModelProperty(value = "檔案類型:私有或公開(1,私 2.共)")
    private Integer type;
	/**檔案url*/
	@Excel(name = "檔案url", width = 15)
    @ApiModelProperty(value = "檔案url")
    private String url;
	/**檔案字尾*/
	@Excel(name = "檔案字尾", width = 15)
    @ApiModelProperty(value = "檔案字尾")
    private String suffix;
}
           

FileUtil工具類

package org.jeecg.modules.ai.utils;

import cn.hutool.core.util.IdUtil;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;

/**
 * File工具類,擴充 hutool 工具包
 * @author 億校雲
 * @date 2018-12-27
 */
public class FileUtil extends cn.hutool.core.io.FileUtil {

    /**
     * 定義GB的計算常量
     */
    private static final int GB = 1024 * 1024 * 1024;
    /**
     * 定義MB的計算常量
     */
    private static final int MB = 1024 * 1024;
    /**
     * 定義KB的計算常量
     */
    private static final int KB = 1024;

    /**
     * 格式化小數
     */
    private static final DecimalFormat DF = new DecimalFormat("0.00");

    /**
     * MultipartFile轉File
     * @param multipartFile
     * @return
     */
    public static File toFile(MultipartFile multipartFile){
        // 擷取檔案名
        String fileName = multipartFile.getOriginalFilename();
        // 擷取檔案字尾
        String prefix="."+getExtensionName(fileName);
        File file = null;
        try {
            // 用uuid作為檔案名,防止生成的臨時檔案重複
            file = File.createTempFile(IdUtil.simpleUUID(), prefix);
            // MultipartFile to File
            multipartFile.transferTo(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 删除
     * @param files
     */
    public static void deleteFile(File... files) {
        for (File file : files) {
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * 擷取檔案擴充名
     * @param filename
     * @return
     */
    public static String getExtensionName(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot >-1) && (dot < (filename.length() - 1))) {
                return filename.substring(dot + 1);
            }
        }
        return filename;
    }

    /**
     * Java檔案操作 擷取不帶擴充名的檔案名
     * @param filename
     * @return
     */
    public static String getFileNameNoEx(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot >-1) && (dot < (filename.length()))) {
                return filename.substring(0, dot);
            }
        }
        return filename;
    }

    /**
     * 檔案大小轉換
     * @param size
     * @return
     */
    public static String getSize(int size){
        String resultSize = "";
        if (size / GB >= 1) {
            //如果目前Byte的值大于等于1GB
            resultSize = DF.format(size / (float) GB) + "GB   ";
        } else if (size / MB >= 1) {
            //如果目前Byte的值大于等于1MB
            resultSize = DF.format(size / (float) MB) + "MB   ";
        } else if (size / KB >= 1) {
            //如果目前Byte的值大于等于1KB
            resultSize = DF.format(size / (float) KB) + "KB   ";
        } else {
            resultSize = size + "B   ";
        }
        return resultSize;
    }
}
           

Java壓縮圖檔案例2

把圖檔轉換成流檔案

需要再添加個依賴

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>
           

ImageTool工具類

package me.zhengjie.utils;

import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.imageio.stream.FileImageOutputStream;
import java.io.*;
import java.net.URL;

/**
 *  圖檔處理類
 *
 * @author honghe
 */
public class ImageTool {

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

    private static final long DEFAULT_SIZE = 200 * 1024;


    /*public static void main(String[] args) {
        String picUrl = "C:/Users/admin/Desktop/url/picture/═╝╞м20.png";
        compress(picUrl,50);
    }*/

    /**
     * @param picUrl 圖檔位址
     * @param desFileSize 壓縮大小
     */
    public static byte[] compress(String picUrl, long desFileSize,String fileNameCopy) {
        try {
            URL url = new URL(picUrl);
            byte[] imageBytes = IOUtils.toByteArray(url);
            if (imageBytes == null || imageBytes.length <= 0) {
                logger.error("圖檔資源加載失敗,請檢查連結位址!url:" + picUrl);
                throw new RuntimeException("圖檔資源加載失敗,請檢查連結位址!url:" + picUrl);
            }
            if (imageBytes.length <= desFileSize * 1024) {
                return imageBytes;
            }
            byte[] imageBytesCopy = imageBytes;
            double accuracy = getAccuracy(imageBytes.length / 1024);
            while (imageBytes.length > desFileSize * 1024) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            logger.info("【圖檔壓縮】 | 圖檔原大小={}kb | 壓縮後大小={}kb | url={}",
                    imageBytesCopy.length / 1024, imageBytes.length / 1024, picUrl);
            File file = new File(fileNameCopy);
            if(!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter =new FileWriter(file);
            fileWriter.write("");
            fileWriter.flush();
            fileWriter.close();

            FileImageOutputStream imageOutput = new FileImageOutputStream(new File(fileNameCopy));
            imageOutput.write(imageBytes, 0, imageBytes.length);
            imageOutput.close();
            return imageBytes;
        } catch (Exception e) {
            logger.error("圖檔資源加載失敗,請檢查連結位址!url:" + picUrl);
            throw new RuntimeException("圖檔資源加載失敗,請檢查連結位址!url:" + picUrl);
        }
    }

    /**
     * 對圖檔byte[]進行base64
     */
    public static String base64ByPic(byte[] imageBatys) {
        // 對位元組數組Base64編碼
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(imageBatys);
    }

    /**
     * base64字元串轉換成圖檔
     * 對位元組數組字元串進行Base64解碼并生成圖
     *
     * @param imgStr      base64字元串
     * @param imgFilePath 圖檔存放路徑
     * @dateTime 2018-02-23 14:42:17
     */
    public static boolean Base64ToImage(String imgStr, String imgFilePath) throws IOException {
        OutputStream out = null;
        if (StringUtils.isEmpty(imgStr)) {
            return false;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解碼
            byte[] b = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {// 調整異常資料
                    b[i] += 256;
                }
            }
            out = new FileOutputStream(imgFilePath);
            out.write(b);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 自動調節精度(經驗數值)
     *
     * @param size 源圖檔大小
     * @return 圖檔壓縮品質比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < 900) {
            accuracy = 0.85;
        } else if (size < 2047) {
            accuracy = 0.6;
        } else if (size < 3275) {
            accuracy = 0.44;
        } else {
            accuracy = 0.4;
        }
        return accuracy;
    }
}
           

上傳圖檔Controller和Service

/**
     * 上傳圖檔
     * @param file
     * @return
     * @throws Exception
     */
    @Log("上傳圖檔")
    @PostMapping(value = "/picturesProduct")
    public ResponseEntity picturesProduct(@RequestParam MultipartFile file, String productUrl){
        Picture picture = pictureProductService.uploadZip(file,SecurityUtils.getUsername());
        Map map = new HashMap();
        map.put("errno",0);
        map.put("id",picture.getId());
        map.put("data",new String[]{picture.getUrl()});
        return new ResponseEntity(map,HttpStatus.OK);
    }



   /**
     * 上傳圖檔
     * @param file
     * @param username
     * @return
     */
    @CacheEvict(allEntries = true)
    Picture uploadZip(MultipartFile file, String username);
           

Impl

@Override
    @Transactional(rollbackFor = Throwable.class)
    public Picture uploadZip(MultipartFile file, String username) {
        String originalFilename = "";//檔案名稱
        String originalFilenameBackup = "";//檔案名稱備份
        // 檔案儲存路徑
        String filePatha = pictureUrl;
        String filePathaBackup = pictureUrl;
        String urlProduct = "";
        Picture picture = new Picture();
        try {
            File addFile = new File(filePatha);
            if (!addFile.exists()) {
                addFile.mkdirs();
            }
            //如果出現同名檔案,檔案名後面加随機數
            String findFfilePatha = filePatha + "/" + file.getOriginalFilename();
            File addNameFile = new File(findFfilePatha);
            if (!addNameFile.exists()) {
                originalFilename = FileUtil.getFileNameNoEx(file.getOriginalFilename())+"."+ FileUtil.getExtensionName(file.getOriginalFilename());
                originalFilenameBackup = FileUtil.getFileNameNoEx(file.getOriginalFilename())+"Backup."+ FileUtil.getExtensionName(file.getOriginalFilename());
            }else{
                originalFilename = FileUtil.getFileNameNoEx(file.getOriginalFilename()) + System.currentTimeMillis() +"."+ FileUtil.getExtensionName(file.getOriginalFilename());
                originalFilenameBackup = FileUtil.getFileNameNoEx(file.getOriginalFilename()) + System.currentTimeMillis() +".Backup"+ FileUtil.getExtensionName(file.getOriginalFilename());

            }
            filePatha += "/" + originalFilename;//絕對路徑
            filePathaBackup += "/" + originalFilenameBackup;//備份檔案
            urlProduct += "/" + originalFilename;//相對路徑
            // 轉存檔案
            log.info("位址:" + filePatha);
            log.info("檔案大小:" + new BigDecimal((double)file.getSize()/1024/1024).divide(new BigDecimal(1),2,BigDecimal.ROUND_HALF_UP)+"M");
            log.info("檔案名" + originalFilename);
            File file_a = FileUtil.toFile(file);

            BigDecimal a = new BigDecimal((double)file.getSize()/1024/1024).divide(new BigDecimal(1),2,BigDecimal.ROUND_HALF_UP);
            BigDecimal b = new BigDecimal (0.3);//超過0.3M的圖檔進行壓縮
            File file_b = new File(filePatha);
            if(a.compareTo(b) == 1){//a大于b,壓縮圖檔
                //壓縮圖檔
                //第一種方法 部分格式圖檔會壓縮失敗
                //Thumbnails.of(file_a.getAbsolutePath()).scale(1f).outputQuality(0.25f).toFile(file_b.getAbsolutePath());

                //第二種 流檔案
                File file_c = new File(filePathaBackup);//備份資料
                Files.copy(file_a.toPath(),file_c.toPath());//不壓縮直接拷貝
                ImageTool.compress("file:" + filePathaBackup,2000,file_b.getAbsolutePath());
                log.info("壓縮成功:" + file_b.getAbsolutePath());
            }else{
                Files.copy(file_a.toPath(),file_b.toPath());//不壓縮直接拷貝
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        picture.setCreateTime(new Timestamp(System.currentTimeMillis()));
        picture.setSize(new BigDecimal((double)file.getSize()/1024/1024).divide(new BigDecimal(1),2,BigDecimal.ROUND_HALF_UP)+"M");
        picture.setUsername(username);
        picture.setFilename(originalFilename);
        picture.setUrl("/static/picture" + urlProduct);//存本地相對位址
        picture.setDelete(filePatha);
        pictureRepository.save(picture);
        return picture;
    }
           

按指定大小把圖檔進行縮放(會遵循原圖高寬比例)

//按指定大小把圖檔進行縮和放(會遵循原圖高寬比例) 
/此處把圖檔壓成400×500的縮略圖
Thumbnails.of(fromPic).size(400,500).toFile(toPic);//變為400*300,遵循原圖比例縮或放到400*某個高度
           

按照指定比例進行縮小和放大

//按照比例進行縮小和放大
Thumbnails.of(fromPic).scale(0.2f).toFile(toPic);//按比例縮小
Thumbnails.of(fromPic).scale(2f);//按比例放大
           

按指定的大小進行縮放(不遵循原圖比例)

//不按比例,就按指定的大小進行縮放
Thumbnails.of(fromPic).size(100, 100).keepAspectRatio(false).toFile(toPic);
//或者Thumbnails.of(fromPic).forceSize(100,100).toFile(toPic);
           

旋轉圖檔

//旋轉圖檔,rotate(角度),正數則為順時針,負數則為逆時針
Thumbnails.of(fromPic).size(200,200).rotate(90).toFile(toPic);
           

圖檔尺寸不變,壓縮圖檔檔案大小

//圖檔尺寸不變,壓縮圖檔檔案大小outputQuality實作,參數1為最高品質
Thumbnails.of(fromPic).scale(1f).outputQuality(0.25f).toFile(toPic);
           

給圖檔加水印

//給圖檔加水印,watermark(位置,水印圖,透明度)Positions.CENTER表示加在中間
Thumbnails.of(fromPic).size(400,400)
	.watermark(Positions.CENTER,ImageIO.read(waterPic),0.5f)
	.outputQuality(0.8f).toFile(toPic);
           

圖檔裁剪

//用sourceRegion()實作圖檔裁剪
//圖檔中心300*300的區域,Positions.CENTER表示中心,還有許多其他位置可選
Thumbnails.of(fromPic).sourceRegion(Positions.CENTER,300,300)
	.size(300,300).toFile(toPic);
           
//圖檔中上區域300*300的區域
Thumbnails.of(fromPic).sourceRegion(Positions.TOP_CENTER,300,300)
	.size(300,300).toFile(toPic);
           

轉換圖檔格式 

格式由jpg變為png,圖檔大小沒變。

//用outputFormat(圖像格式)轉換圖檔格式,保持原尺寸不變
Thumbnails.of(fromPic).scale(1f).outputFormat("png")
	.toFile("picture/png格式的圖檔.png");
           

輸出成檔案流OutputStream

//輸出成檔案流OutputStream
OutputStream os=new FileOutputStream(toPic);
Thumbnails.of(fromPic).size(120,120).toOutputStream(os);
           

輸出成BufferedImage

//輸出BufferedImage,asBufferedImage()傳回BufferedImage
BufferedImage bi=Thumbnails.of(fromPic).size(120,120).asBufferedImage();
ImageIO.write(bi,"jpg",toPic);
           

壓縮至指定圖檔尺寸,保持圖檔不變形,多餘部分裁剪掉

//壓縮至指定圖檔尺寸(例如:橫400高300),保持圖檔不變形,多餘部分裁剪掉(這個是引的網友的代碼)
		BufferedImage image = ImageIO.read(fromPic);
		Builder<BufferedImage> builder = null;
 
		int imageWidth = image.getWidth();
		int imageHeitht = image.getHeight();
		if ((float)300 / 400 != (float)imageWidth / imageHeitht) {
			if (imageWidth > imageHeitht) {
				image = Thumbnails.of(fromPic).height(300).asBufferedImage();
			} else {
				image = Thumbnails.of(fromPic).width(400).asBufferedImage();
			}
			builder = Thumbnails.of(image).sourceRegion(Positions.CENTER, 400, 300).size(400, 300);
		} else {
			builder = Thumbnails.of(image).size(400, 300);
		}
		builder.outputFormat("jpg").toFile(toPic);
           

參考作者:https://blog.csdn.net/chenleixing/article/details/44685817

Java圖檔壓縮大小,圖檔縮放