Java代码实现图片上传压缩

xieshuoshuo 发布于 2024-05-29 403 次阅读 预计阅读时间: 4 分钟


因为是拿别人的开源项目进行二开,所以对于一些原有的需要进行改进,就比如说图片上传到服务器的功能。原有的项目是已经实现了图片上传的服务器的功能,但是其前端限制的上传大小是5MB,并且对于小于5MB的图片没有进行处理,这导致我们在开发并部署到自己服务器的时候,部分图片加载非常慢,很影响用户体验,所以,我们需要把上传的图片进行压缩才可以。

由于之前对Java开放了解的比较少,所以网上找的时候,选择了Thumbnailator作为图片压缩的首选。

首先是原代码实现图片上传到服务器的基本逻辑:

/* 控制器 */
@PostMapping("/upload")
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
    MultipartFile file = uploadReqVO.getFile();
    String path = uploadReqVO.getPath();
    return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
}

/* 方法定义 */
/**
* 保存文件,并返回文件的访问路径
*
* @param name    文件名称
* @param path    文件路径
* @param content 文件内容
* @return 文件路径
*/
String createFile(String name, String path, byte[] content);

/* 方法实现 */
@Override
@SneakyThrows
public String createFile(String name, String path, byte[] content) {
    // 计算默认的 path 名
    String type = FileTypeUtils.getMineType(content, name);
    if (StrUtil.isEmpty(path)) {
        path = FileUtils.generatePath(content, name);
    }
    // 如果 name 为空,则使用 path 填充
    if (StrUtil.isEmpty(name)) {
        name = path;
    }

    // 上传到文件存储器
    FileClient client = fileConfigService.getMasterFileClient();
    Assert.notNull(client, "客户端(master) 不能为空");
    String url = client.upload(content, path, type);

    // 保存到数据库
    FileDO file = new FileDO();
    file.setConfigId(client.getId());
    file.setName(name);
    file.setPath(path);
    file.setUrl(url);
    file.setType(type);
    file.setSize(content.length);
    fileMapper.insert(file);
    return url;
}

/**********文件存储器**********/
/*方法定义*/
/**
 * 获得 Master 文件客户端
 *
 * @return 文件客户端
 */
FileClient getMasterFileClient();

/*方法实现*/

private static final Long CACHE_MASTER_ID = 0L;

@Override
public FileClient getMasterFileClient() {
    return clientCache.getUnchecked(CACHE_MASTER_ID);
}

/**********application.yaml**********/
  # Servlet 配置
  servlet:
    # 文件上传相关配置项
    multipart:
      max-file-size: 16MB # 单个文件大小
      max-request-size: 300MB # 设置总上传的文件大小

接下来是修改后的,首先要在pom.xml文件上引用

<dependency>
      <groupId>net.coobird</groupId>
      <artifactId>thumbnailator</artifactId>
      <version>0.4.14</version>
</dependency>

然后新建一个ImageCompressionUtils文件,内容如下

import net.coobird.thumbnailator.Thumbnails;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ImageCompressionUtils {
    public static byte[] compressImage(byte[] imageBytes, double scale, double quality) throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        Thumbnails.of(inputStream)
                .scale(scale)
                .outputQuality(quality)
                .toOutputStream(outputStream);

        return outputStream.toByteArray();
    }

    public static byte[] compressImageMax(byte[] imageBytes, double maxFileSizeKB) throws IOException {
        double quality = 1.0;
        byte[] compressedBytes = imageBytes;
        ByteArrayOutputStream outputStream;

        while (true) {
            outputStream = new ByteArrayOutputStream();
            Thumbnails.of(new ByteArrayInputStream(imageBytes))
                    .scale(1.0)
                    .outputQuality(quality)
                    .toOutputStream(outputStream);
            compressedBytes = outputStream.toByteArray();

            if (compressedBytes.length / 1024 <= maxFileSizeKB || quality <= 0.1) {
                break;
            }
            quality -= 0.1;
        }

        return compressedBytes;
    }
}

然后修改方法的实现类:

@Override
@SneakyThrows
public String createFile(String name, String path, byte[] content) {
    // 计算默认的 path 名
    String type = FileTypeUtils.getMineType(content, name);
    if (StrUtil.isEmpty(path)) {
        path = FileUtils.generatePath(content, name);
    }
    // 如果 name 为空,则使用 path 填充
    if (StrUtil.isEmpty(name)) {
        name = path;
    }

    // 检查文件类型,若为图片则进行最大压缩
    if (type.startsWith("image/")) {
        content = ImageCompressionUtils.compressImageMax(content, 150); // 将图片压缩到小于150KB
    }

    // 上传到文件存储器
    FileClient client = fileConfigService.getMasterFileClient();
    Assert.notNull(client, "客户端(master) 不能为空");
    String url = client.upload(content, path, type);

    // 保存到数据库
    FileDO file = new FileDO();
    file.setConfigId(client.getId());
    file.setName(name);
    file.setPath(path);
    file.setUrl(url);
    file.setType(type);
    file.setSize(content.length);
    fileMapper.insert(file);
    return url;
}

我这里是将图片压缩至150KB以内,最终实现: