前后端分离-实现生成文件压缩包

326 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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;
}

临时文件还是有点麻烦,大佬们还有其他办法吗?

如果有,欢迎留言指导哦~