我的需求是一个可以批量下载文件或文件夹的接口,下载一个文件就正常下载,下载多个文件或单个多个文件夹都压缩成zip下载
本来想的是直接用hutool里面的ziputil工具类就行,但是我这里报错的文件都是用随机字符串命名的,直接用ZipUtil.zip方法下载下来的压缩包中文件都是原本文件的随机字符串名称
下面是我的处理方式,其中的一些工具类和压缩相关的类还是用的hutool中的,只不过没有用 ZipUtil封装好的方法
java
@Override
@OperLog(type = OperType.DOWNLOAD)
public void download(List<String> ids) {
List<WebDisk> webDiskList = this.webDiskMapper.selectList(new QueryWrapper<WebDisk>().in("id", ids));
if (webDiskList.isEmpty()) {
throw new RuntimeException("文件不存在,下载失败");
}
if (webDiskList.size() == 1 && webDiskList.get(0).isFile()) {
WebDisk webDisk = webDiskList.get(0);
try (FileInputStream is = new FileInputStream(this.getFullPath(webDisk));
ServletOutputStream os = response.getOutputStream()) {
response.setContentType(request.getSession().getServletContext().getMimeType(webDisk.getFileSuffix())); // 获取文件的mimetype
response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode(webDisk.getOriginName(), "UTF-8"));
IoUtil.copy(is, os);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("下载失败,系统内部错误");
}
} else {
response.setContentType(request.getSession().getServletContext().getMimeType("zip")); // 获取文件的mimetype
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
response.setHeader("content-disposition", "attachment;fileName=" +
URLEncoder.encode(FileNameUtil.mainName(webDiskList.get(0).getOriginName()) + ".zip", "UTF-8"));
webDiskList.forEach(webDisk -> {
try {
this.addFileToZip(webDisk, zos, "", new HashMap<>());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
主要的方法就是下面这个 addFileToZip方法
因为上传文件的时候用随机字符串命名就是为了防止文件名称重复,所以这里打压缩包的时候给他们都设置为上传时候的原名称,但是为了防止名称重复,以名称后面累加括号加数字的方式重命名了
因为我上传的时候校验了不能上传重复名称的文件夹,所以我这里就只处理了重名的文件
java
private void addFileToZip(WebDisk webDisk, ZipOutputStream zos, String path, Map<String, Integer> nameCount) throws IOException {
String filePath = this.getFullPath(webDisk);
File file = FileUtil.file(filePath);
String curFilePath = path + (webDisk.isFile() ? webDisk.getOriginName() : (webDisk.getOriginName() + "/"));
ZipEntry zipEntry = new ZipEntry(curFilePath);
try {
zos.putNextEntry(zipEntry);
} catch (ZipException e) {
// 有重复名称文件
String mainName = FileNameUtil.mainName(curFilePath);
String extName = FileNameUtil.extName(curFilePath);
int num = MapUtil.getInt(nameCount, curFilePath, 0) + 1;
String newFilePath = path + mainName + "(" + num + ")" + "." + extName;
zipEntry = new ZipEntry(newFilePath);
zos.putNextEntry(zipEntry);
nameCount.put(curFilePath, num);
}
if (file.isFile()) {
try (FileInputStream is = new FileInputStream(file)) {
IoUtil.copy(is, zos);
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
this.webDiskMapper.selectList(new QueryWrapper<WebDisk>().eq("pid", webDisk.getId()).eq("is_delete", 0))
.forEach(sub -> {
try {
this.addFileToZip(sub, zos, webDisk.getOriginName() + File.separator, nameCount);
} catch (IOException e) {
e.printStackTrace();
}
});
}
zos.closeEntry();
}
需要注意的是我这里目录的层级关系已经维护到表里了,所以递归找下级的时候直接查的库