Springboot实现后端打包压缩包功能
需求
前端发送请求数组,后端返回请求集合的压缩文件。
步骤梳理
实现
首先封装一个公共的构造ZipFile的类
public class ZipEntityUtil {
public static void downloadZipFiles(Map<String,File> fileMap, HttpServletResponse response) throws IOException {
//设置response的Header
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"download.zip\"");
//创建输出流
ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());
//循环将每个文件写入压缩包
for (Map.Entry<String,File> entry : fileMap.entrySet()) {
FileInputStream fis = new FileInputStream(entry.getValue());
ZipEntry zipEntry = new ZipEntry(entry.getKey());
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
}
//关闭输出流
zipOut.close();
}
}
在此构造的方法中,我希望传入文章的名字和对应的文件,还有response,让函数帮我把文件写入response。
然后再在controller层写一个API
@RequestMapping(value = "exportPDFbyIds",method = RequestMethod.GET)
@ApiOperation(value = "根据文献ID导出PDF",notes = "根据文献ID导出PDF")
public void exportPDFbyIds(@RequestParam(required = true)String[] LiteratureIds,HttpServletResponse response) throws IOException {
//从文献ID数组中取到每篇文献对应的附件数组
List<Literature> literatureList = literatureMapper.exportLiteratureById(LiteratureIds);
System.out.println(literatureList);
if (literatureList.isEmpty()) {
//构造response返回json
response.setContentType("application/json;charset=utf-8");
response.setStatus(500);
PrintWriter writer = response.getWriter();
writer.write("{\"resultCode\":500,\"message\":\"抱歉,所选文献不存在附件!\"}");
writer.flush();
writer.close();
return;
}
// 构建附件ID列表
List<Integer> AnnexIds = literatureList.stream().map(Literature::getAnnexId).collect(Collectors.toList());
List<LiteratureAnnex> literatureAnnexList = literatureAnnexService.getLiteratureAnnexByIds(AnnexIds);
Map<String,File> fileMap = literatureAnnexList.stream().collect(Collectors.toMap(LiteratureAnnex::getTitle,literatureAnnex -> new File(literatureAnnex.getFilePath())));
try{
ZipEntityUtil.downloadZipFiles(fileMap,response);
}catch (IOException e){
e.printStackTrace();
response.setContentType("application/json;charset=utf-8");
response.setStatus(500);
PrintWriter writer = response.getWriter();
writer.write("{\"resultCode\":500,\"message\":\"抱歉,获取文件失败!!\"}");
writer.flush();
writer.close();
}
}
前端代码将按钮绑定在axios请求上,点击后开始下载
axios({
method:"GET",
url:"/api/exportPDFbyIds",
responseType:'blob',
headers:{
'Content-Type': 'application/zip'
},
params:{
LiteratureIds:selectedIds + ''
},
}).then(
res =>{
if (res.status === 200){
// 具体思路是模拟一个dom节点,把下载事件绑定在该节点并模拟点击事件。
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement("a")
link.href = url
link.setAttribute('download','download.zip')
document.body.appendChild(link)
link.click()
}
}
)
收获
-
Controller层在返回ZIP类型的非JSON响应的时候,可以自己构造
HttpServletResponse
来进行个性化的响应。 -
从List
中的某个字段构造键值对的时候,可以使用Java8的Lamda来快速构建,无需使用for循环这种麻烦的用法。
Map<String,File> fileMap = literatureAnnexList.stream().collect(Collectors.toMap(LiteratureAnnex::getTitle,literatureAnnex -> new File(literatureAnnex.getFilePath())));
- 实现返回前端的方案不止一种,也可以使用打包在本地的方法并且返回给前端绝对路径,并定时清除生成目录。个人权衡之后认为本文的方法更好。