开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情 >>吾日三省吾身:好好吃饭、好好睡觉、好好活着。
实现生成文件压缩包(可设置解压密码)
简单描述一下要实现的功能——前端页面存在一个压缩按钮,点击压缩去访问后端提供的压缩API,然后浏览器成功下载压缩文件【看起来好像很简单,实际操作下来你会发现还确实很简单的】前端代码示例:
<template>
<div>
<Button @click="zipImg" type="primary">导出</Button>
</div>
</template>
<script>
export default {
name: "zipFileImg",
data() {
return {};
},
methods: {
zipImg() {
let url = "http://localhost:8080/util/zipNetworkFile";
const link = document.createElement('a');
link.href = url;
link.click();
},
}
}
</script>
<style scoped>
</style>
前端主要使用a标签点击事件,访问后端 http://localhost:8080/util/zipNetworkFile 接口进行浏览器下载效果。重点是如何实现 zipNetworkFile 接口。
【Java实现方法——net.lingala.zip4j】Maven项目就优先使用Maven依赖,所以zip4j很符合今日需求
第一步:项目添加Maven依赖
<!-- https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j -->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
可以根据项目添加不同版本的zip4j。
第二步:实现zipNetworkFile接口
public void zipNetworkFile(HttpServletResponse response) {
try {
response.setContentType("application/octet-stream; charset=UTF-8");
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("a-test.zip", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//设置压缩文件参数
ZipParameters zipParameters = new ZipParameters();
//设置压缩方法
zipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
//设置压缩级别
zipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
//设置压缩文件加密
zipParameters.setEncryptFiles(true);
//设置加密方法
zipParameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
//设置aes加密强度
zipParameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
//设置密码
zipParameters.setPassword("123");
String localFileUrl = "test.zip";
ArrayList<File> files = new ArrayList<>();
// 返回
BufferedInputStream bufferedInputStream = null;
ServletOutputStream servletOutputStream = null;
try {
//创建压缩文件
ZipFile zipFile = new ZipFile(localFileUrl);
String imgUrl = "https://pic.netbian.com/uploads/allimg/221201/005454-1669827294d312.jpg";
String imgName = "test.jpg";
File file = getFile(imgUrl, imgName);
files.add(file);
// 添加文件到压缩文件
zipFile.addFiles(files, zipParameters);
bufferedInputStream = new BufferedInputStream(new FileInputStream(localFileUrl));
servletOutputStream = response.getOutputStream();
int len;
byte[] bytes = new byte[1024 * 2];
while ((len = bufferedInputStream.read(bytes)) != -1) {
servletOutputStream.write(bytes, 0, len);
}
response.addHeader("Content-Length", String.valueOf(bytes.length));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (servletOutputStream != null) {
servletOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (new File(localFileUrl).exists()) {
try {
Files.delete(Paths.get(localFileUrl));
} catch (IOException e) {
e.printStackTrace();
}
}
for (File file : files) {
file.delete();
}
}
}
因为是使用zip4j依赖,所以有完整的实现类,完全不担心。ZipParameters类主要用来配置生成压缩包参数,例如zipParameters.setPassword("123");就是设置压缩包密码,如果不需要密码就可以设置password参数。
以下是一些注意点和问题点:
1.设置压缩包名称也会不成功
上述代码中第一个try-catch处,response设置Content-Disposition响应信息一定要在.write之前,不然前端响应体会没有Content-Disposition。然后filename="filename.zip"设置压缩包名称也会不成功。
2.new ZipFile()生成的文件删除
new ZipFile("test.zip")会生成一个文件test.zip,乍一看感觉会有点重复,但是如果你接口需要放服务器,那么"test.zip"和a标签click下载的"test.zip"就不一样了。
3.files.add(file)时,getFile()生成的临时文件也需要删除
主要还是临时文件需要删除,避免占用磁盘空间和内存。下面放一下getFile()代码
public File getFile(String fileUrl, String fileName) {
// fileName文件名处理
if (fileName.contains("/")) {
fileName = fileName.replace("/", "-");
}
File imageFile = null;
try {
//定义一个URL对象,就是你想下载的URL地址
URL url = new URL(fileUrl);
//打开连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置请求方式为"GET"
conn.setRequestMethod("GET");
//超时响应时间为10秒
conn.setConnectTimeout(10 * 1000);
//通过输入流获取数据
InputStream is = conn.getInputStream();
//得到二进制数据,以二进制封装得到数据,具有通用性
byte[] data = readInputStream(is);
//创建一个文件对象用来保存
imageFile = new File(fileName);
//创建输出流
FileOutputStream outStream = new FileOutputStream(imageFile);
//写入数据
outStream.write(data);
//关闭输出流,释放资源
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return imageFile;
}
public static byte[] readInputStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
byte[] data = outStream.toByteArray();
outStream.close();
inStream.close();
return data;
}
临时文件还是有点麻烦,大佬们还有其他办法吗?
如果有,欢迎留言指导哦~