压缩MultipartFile

复制代码
原因:上传MultipartFile到第三方提示像素要在300*300以内
public com.alibaba.fastjson2.JSONObject upload(@RequestPart("file") MultipartFile file){
file = compressImageIfNeededNoTemp(file, 300, 300);
}

一:压缩方法不产生临时文件

复制代码
private MultipartFile compressImageIfNeededNoTemp(MultipartFile file, int maxWidth, int maxHeight) {
    try {
        // 1. 先读取文件内容到内存(此时会占用 Tomcat 临时文件,但流会关闭)
        byte[] fileBytes = file.getBytes();

        // 2. 从内存中读取图片(不再依赖临时文件)
        try (ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes)) {
            BufferedImage image = ImageIO.read(bais);

            if (image == null) {
                throw new RuntimeException("无法解析图片格式");
            }

            int width = image.getWidth();
            int height = image.getHeight();

            // 如果已经符合要求,返回基于内存的 MultipartFile
            if (width <= maxWidth && height <= maxHeight) {
                // 返回内存文件,不再持有原始 file 的引用
                return new CustomMultipartFile(
                        file.getName(),
                        file.getOriginalFilename(),
                        file.getContentType(),
                        fileBytes
                );
            }

            // 计算缩放比例
            double scale = Math.min(
                    (double) maxWidth / width,
                    (double) maxHeight / height
            );

            int newWidth = (int) (width * scale);
            int newHeight = (int) (height * scale);

            int imageType = image.getType() == BufferedImage.TYPE_CUSTOM
                    ? BufferedImage.TYPE_INT_RGB
                    : image.getType();

            BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, imageType);
            Graphics2D g = resizedImage.createGraphics();

            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            g.drawImage(image, 0, 0, newWidth, newHeight, null);
            g.dispose();

            image.flush();

            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                String format = getImageFormat(file.getOriginalFilename());
                boolean written = ImageIO.write(resizedImage, format, baos);
                if (!written) {
                    format = "jpg";
                    ImageIO.write(resizedImage, format, baos);
                }
                resizedImage.flush();

                return new CustomMultipartFile(
                        file.getName(),
                        file.getOriginalFilename(),
                        file.getContentType(),
                        baos.toByteArray()
                );
            }
        }

    } catch (IOException e) {
        throw new RuntimeException("图片压缩失败: " + e.getMessage(), e);
    }
}

二:文件格式

复制代码
private String getImageFormat(String filename) {
    if (filename == null) return "jpg";
    String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
    return "jpg".equals(ext) || "jpeg".equals(ext) ? "jpg" : ext;
}

三:返回工具类

复制代码
package com.edu.service.util;

import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;

/**
 * 自定义 MultipartFile 实现,用于生产环境
 */
public class CustomMultipartFile implements MultipartFile {
    
    private final String name;
    private final String originalFilename;
    private final String contentType;
    private final byte[] content;
    
    public CustomMultipartFile(String name, String originalFilename, 
                               String contentType, byte[] content) {
        this.name = name;
        this.originalFilename = originalFilename;
        this.contentType = contentType;
        this.content = content != null ? content : new byte[0];
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getOriginalFilename() {
        return originalFilename;
    }
    
    @Override
    public String getContentType() {
        return contentType;
    }
    
    @Override
    public boolean isEmpty() {
        return content.length == 0;
    }
    
    @Override
    public long getSize() {
        return content.length;
    }
    
    @Override
    public byte[] getBytes() throws IOException {
        return content;
    }
    
    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(content);
    }
    
    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        try (FileOutputStream fos = new FileOutputStream(dest)) {
            fos.write(content);
        }
    }
}