
在处理批量图片上传的场景时,通过压缩包一次性上传是个高效的选择。过去我常用zip4j来处理压缩包,后来发现国产的Hutool工具包里也有封装好的ZipUtil,它是对java.util.zip的封装,功能足够且能减少一个外部依赖,于是便切换了过来。
本文将通过一个实际案例,演示如何使用SpringBoot结合Hutool,实现一个接收图片压缩包、自动解压、校验文件并与数据库联动的完整接口。这个案例的背景是管理用户的人脸照片。
一、构建文件上传接口
首先,我们需要创建一个用于接收压缩包的POST接口。
1. 接口定义与参数接收
在控制器类上使用@RestController注解,并通过@PostMapping定义接口路径。接口接收类型为multipart/form-data的表单数据,使用@RequestParam获取名为“zipFile”的文件参数。
@RestController
public class Controller {
/**
* 压缩包文件上传
*
* @param zipFile 压缩包文件
* @return R 返回实体类
*/
@PostMapping("/upload")
public R upload(@RequestParam("zipFile") MultipartFile zipFile) {
//TODO
return R.success();
}
}
这个接口的默认访问地址是:http://127.0.0.1:8080/upload。
2. 配置文件路径与初始化
我们需要从配置文件中读取文件存储的根路径。使用@Value注解注入配置值,同时别忘记添加@Component注解以确保能正确扫描到配置。
//组件扫描注解,用于获取配置文件内容
@Component
@RestController
public class Controller {
//获取配置文件中的文件上传物理路径,例:C:/upload/
@Value("${config.uploadPath}")
private String uploadPath;
/**
* 压缩包文件上传
*
* @param zipFile 压缩包文件
* @return R 返回实体类
*/
@PostMapping("/upload")
public R upload(@RequestParam("zipFile") MultipartFile zipFile) {
//获取文件全名
String fileName = zipFile.getOriginalFilename();
//解压目标文件夹对象(压缩文件解压到此文件夹中)
File extractFolder = new File(uploadPath + "extract/");
//压缩包存储目标文件对象
File destFile = new File(uploadPath + fileName);
//文件上传路径对象
File fileDirectory = new File(uploadPath);
//当上传路径不存在时,生成上传路径
if (!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
//TODO
return R.success();
}
}
二、实现压缩包的解压与业务处理
上传文件后,核心逻辑是解压并处理其中的文件。我们将这部分逻辑抽离成一个独立的方法。
1. 核心解压与校验方法
这个方法负责三步操作:保存上传的压缩包、解压到指定目录、校验解压出的文件。
/**
* 处理压缩包文件
*
* @param zipFile 上传压缩包
* @param destFile 指定压缩包路径
* @param extractFolder 解压后文件夹
* @return R 返回实体类
*/
private R dealZip(MultipartFile zipFile, File destFile, File extractFolder) {
//判断解压后文件夹是否存在
if (!extractFolder.exists()) {
//不存在就创建
extractFolder.mkdirs();
}
try {
//步骤1、把上传的压缩包文件保存到指定压缩包路径
zipFile.transferTo(destFile);
} catch (IOException e) {
//运行报错直接返回错误信息
return R.failed(e.getMessage());
}
//步骤2、调用Hutool的ZipUtil压缩工具类的unzip方法来进行对压缩包文件的解压,解压到指定目录
ZipUtil.unzip(destFile, extractFolder);
//解压缩完删除原文件(可不删)
destFile.delete();
//步骤3、获取解压后目录下所有的文件
File[] images = extractFolder.listFiles();
//这边对获取到的文件数组进行判空校验
if (images == null || images.length == 0) {
//不存在就把压缩文件夹删除(可不删)
extractFolder.delete();
return failed("上传失败,压缩包为空");
}
//错误文件集合
List<String> nameList = new ArrayList<>();
//这边简单做个jpg图片校验,单个文件全名中不包含jpg的文件添加到错误集合中
stream(images).forEach(image -> {
if (!image.getName().contains("jpg")) {
nameList.add(image.getName());
}
});
//错误集合存在数据就返回错误的文件名集合
if (nameList.size() > 0) {
//存在错误图片就把压缩文件夹删除(可不删)
extractFolder.delete();
return failed("压缩包文件错误,错误文件如下:" + nameList + ",请修改后再上传");
}
//错误集合长度为0时返回解压后文件数组对象
return success(images);
}
2. 在接口中整合业务逻辑
现在,在upload接口中调用上面的方法,并加入具体的业务逻辑。本案例中,我们假设图片文件名前缀对应数据库中的用户ID。
//组件扫描注解,用于获取配置文件内容
@Component
@RestController
public class Controller {
//获取配置文件中的文件上传物理路径,例:C:/upload/
@Value("${config.uploadPath}")
private String uploadPath;
//Service层
@Resource
private Service service;
/**
* 压缩包文件上传
*
* @param zipFile 压缩包文件
* @return R 返回实体类
*/
@PostMapping("/upload")
public R upload(@RequestParam("zipFile") MultipartFile zipFile) {
//获取文件全名
String fileName = zipFile.getOriginalFilename();
//解压目标文件夹对象(压缩文件解压到此文件夹中)
File extractFolder = new File(uploadPath + "extract/");
//压缩包存储目标文件对象
File destFile = new File(uploadPath + fileName);
//文件上传路径对象
File fileDirectory = new File(uploadPath);
//当上传路径不存在时,生成上传路径
if (!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
//调用上面【处理压缩包文件方法】得到返回结果
R zipResult = dealZip(zipFile, destFile, extractFolder);
//失败就返回错误信息
if (FAIL_CODE == zipResult.getCode()) {
return zipResult;
}
//获取【处理压缩包文件方法】中返回的图片文件数组对象
File[] images = (File[]) zipResult.getData();
//实体类集合
List<Entity> entities = new ArrayList<>();
//这边使用stream对图片文件数组对象进行遍历
stream(images).forEach(image -> {
//TODO 这边可以做其他的处理
//这边简单的获取了照片名字的前缀作为id
String id = image.getName().split("\\.")[0];
//通过数据库找找此id的信息
Entity entity = service.getById(id);
if (entity != null) {
//存在此条信息就把此图片拷贝到上传目录中,这边用到Hutool的FileUtil文件工具类的copy文件拷贝方法
FileUtil.copy(image, new File(uploadPath + image.getName()), true);
//存在就添加进实体类集合
entities.add(entity);
}
});
//实体类为空代表在数据库中没有找到对应id的信息
if (entities.size() == 0) {
//删除解压缩目录(可不删)
extractFolder.delete();
return failed("压缩包内图片无匹配信息");
}
//删除解压缩目录(可不删)
extractFolder.delete();
//最后数据库根据id更新所有集合实体类的信息
return service.updateBatchById(entities) ?
success("压缩包上传成功") :
failed("压缩包上传失败");
}
}
三、总结与思考
这个案例的核心是上传人脸照片压缩包,通过解压后以图片文件名前缀作为用户ID,在Java应用中进行匹配和后续存储。在实际开发中,你可以在遍历文件的步骤里加入更复杂的逻辑,比如文件类型深度校验、图像内容分析、分发到不同存储服务等。
回顾整个流程,从接收MultipartFile,到使用Hutool的ZipUtil.unzip()解压,再到结合业务操作数据库,形成了一个清晰的处理闭环。我发现在寻找解决方案时,网上资料往往比较零散,因此自己实践时尽量记录得完整些,既方便自己回顾,也希望能帮到遇到类似问题的朋友。如果你有更好的思路或工具推荐,欢迎在云栈社区交流讨论。