前言
springboot是很流行的框架,文件下载接口也是开发中很简单常见的接口,但是批量附件下载呢?将多个附件打包成zip呢?虽然确实是很简单的一个事情,但是写还是要花点时间来捋一下,前几天刚做过这个需求,就当作笔记记录下来吧
思路
思路其实很简单,我的思路是:
- 随意找一个在当前物理机器上存在的目录,鉴于程序最终运行的坏境可能会有很大不同,如:linux服务器、容器等等,所以动态获取classpath
- 在步骤1中找到的存在的目录下,新建一个临时目录
- 将所要下载的文件复制到此临时目录下
- 使用工具将临时目录打包成一个zip文件
- 使用流将zip文件写回到响应中
- 删除临时目录和zip文件
代码
坏境是springboot项目,另外使用了hutool的一些工具类,springboot依赖项目不能随意变动,此处只贴hutool依赖:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
<scope>compile</scope>
</dependency>
Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
/**
* @Author: liang.qin
* @Date: 2023/2/24 22:13
* @Email: <a href="mailto:liang.qin.work@foxmail.com">liang.qin</a>
* @Description:
**/
@RestController
@RequestMapping("/file/")
public class FileController {
@Autowired
private FileServiceImpl fileService;
/**
* 批量下载附件,打包成zip
* @param response 响应,将流写回
*/
@GetMapping("/down/zip/")
public void downToZip(HttpServletResponse response) throws FileNotFoundException {
fileService.downToZpi(response);
}
}
实现类
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ZipUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
/**
* @Author: liang.qin
* @Date: 2023/2/24 22:17
* @Email: <a href="mailto:liang.qin.work@foxmail.com">liang.qin</a>
* @Description:
**/
@Service
public class FileServiceImpl {
// 批量下载附件,将其打包为zip
public void downToZpi(HttpServletResponse response) throws FileNotFoundException {
// 获取类路径
String classPath = ResourceUtils.getURL("classpath:").getPath();
// 临时目录 以uuid+时间戳 为目录名称
String temp = classPath + IdUtil.simpleUUID() + System.currentTimeMillis();
// 临时目录如果存在则删除
File tempDir = new File(temp);
if (tempDir.exists()) FileUtil.del(tempDir);
// 创建临时目录
boolean mkdir = tempDir.mkdir();
File zipFile = null;
if (mkdir) {
try {
// 此处应根据传递进来的业务参数确定需要下载的物理文件资源,模拟文件资源如下:
List<String> targets = Arrays.asList("D:\\temp\\img1.jpg","D:\\temp\\img2.jpg","D:\\temp\\img3.jpg");
if (CollUtil.isNotEmpty(targets)) {
// 将需要下载的文件复制到临时目录中 TODO 如果此处文件比较多,可采取并行复制
targets.forEach(x->FileUtil.copyFile(new File(x),
// 拼接文件名称
new File(temp+File.separator+x.substring(x.lastIndexOf("\\")+1))));
// 将临时目录压缩成zip
zipFile = ZipUtil.zip(temp);
try (
InputStream in = new FileInputStream(zipFile);
ServletOutputStream os = response.getOutputStream()
) {
int len;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
// 设置响应的一些格式
String encodedFileName = URLEncoder.encode("所有附件.zip", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", encodedFileName));
response.setHeader("Pragma", "no-cache");
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Expires", "0");
}
} catch (RuntimeException e) {
// 此处应该记录日志
e.printStackTrace();
} finally {
// 删除临时目录和压缩后的文件
FileUtil.del(tempDir);
FileUtil.del(zipFile);
}
}
}
}
效果图
调用下载接口
下载zip解压后目录