首页 抖音热门文章正文

Vue + SpringBoot + zip4j实现文件的压缩下载

抖音热门 2025年09月07日 13:28 1 admin

序言

最近在项目当中遇到一个需要解压文件为zip,通过前端进行下载,这和常规的文件下载不同,在 zip 文件还会存在目录,目录中再存放文件,当时找了很多资料都不尽人意,所以自己写了这篇文字供以后参考。SpringBoot 的版本为2.5.15,zip4j 的版本为 2.11.5

Zip4j

Vue + SpringBoot + zip4j实现文件的压缩下载

zip4j是一个功能强大的 Java 库,专门用于处理 ZIP 文件格式。它提供了一系列易于使用的 API,使得开发人员可以方便地在 Java 应用程序中执行各种 ZIP 文件操作,包括创建、读取、更新和提取 ZIP 文件等。GitHub地址 里面说明了各种 API 的使用和示例。

思路

具体思路是打算通过 zip4j 创建一个 zip 文件,然后通过 API 在 zip 文件里面创建目录和文件。当时的业务需求是下载每一个文件的附录文件,每一个文件都会有多个附录文件,不可能去一个个下载,所以需压缩为 zip 格式。具体的代码如下:

/** * 创建 zip 文件 * * @param ids   文件id集合 * @return  zip 文件的路径 */private String createZipFile(String ids) {    Integer[] intArray = Convert.toIntArray(ids);    // 创建一个列表用于存放所有附件文件的路径,后续统一压缩到一个zip文件中    List<String> allFilePaths = new ArrayList<>();    // 循环查询每个id对应的附件文件路径并添加到列表中    // 里面的代码大家可能会觉得麻烦,不用细看,只需要知道这层for循环是为了找到附录文件在磁盘的地址    for (int id : intArray) {        List<SysDownloadCenterAttachment> attachmentList = sysDownloadCenterAttachmentMapper.selectList(new LambdaQueryWrapper<SysDownloadCenterAttachment>()                .eq(SysDownloadCenterAttachment::getDownloadCenterId, id));        if (!CollectionUtils.isEmpty(attachmentList)) {            List<String> filePaths = attachmentList.stream().map(attachment -> {                String filePath = scjtConfig.getUploadPath() + attachment.getLocation();                if (!StringUtils.startsWith(filePath, "D:")) {                    filePath = "D:" + filePath;                }                return filePath;            }).collect(Collectors.toList());            allFilePaths.addAll(filePaths);        }    }    // 创建ZipFile对象,指定zip文件的路径    ZipFile zipFile = new ZipFile("d:/filename.zip");    // 循环附录文件地址    for (String filePath : allFilePaths) {        File file = new File(filePath);        if (file.exists()) {            // 获取文件名(包含后缀)            String fileName = file.getName();            // 这里假设附件所属的id可以通过某种方式获取,比如从文件路径解析或者其他逻辑判断,示例中用一个方法 getFileIdFromPath 来表示获取id的逻辑,你需要根据实际情况替换            String folderNameInsideZip = getFileTitleFromPath(intArray, filePath);            // 构造在zip文件中存放的路径,格式为 "id标识的文件夹名/原文件名"            try {                // 尝试获取目标文件夹在zip文件中的FileHeader                FileHeader folderHeader = zipFile.getFileHeader(folderNameInsideZip + "/");                log.info("文件夹在zip文件中的FileHeader:" + folderHeader);                // 如果zip文件中指定目录不存在,则创建,这一步判断是必须的,不然会导致下一次文件添加动作会覆盖之前的文件                if (Objects.isNull(folderHeader)) {                    new File(folderNameInsideZip).mkdirs();  // 创建文件夹                    ZipParameters folderPar = new ZipParameters();                    folderPar.setCompressionLevel(CompressionLevel.NORMAL);                    folderPar.setEncryptFiles(false);                    zipFile.addFolder(new File(folderNameInsideZip), folderPar);                    log.info("文件夹已成功添加到zip文件中。");                }                // 创建ZipParameters对象,用于设置添加文件的参数                ZipParameters parameters = new ZipParameters();                // 设置压缩方法,这里使用默认压缩方法                parameters.setCompressionMethod(CompressionMethod.DEFLATE);                // 设置压缩级别,这里使用默认压缩级别                parameters.setCompressionLevel(CompressionLevel.NORMAL);                // 设置添加文件在zip文件中的路径,格式为 "文件夹名/文件名"                parameters.setFileNameInZip(folderNameInsideZip + "/" + file.getName());                // 将文件添加到zip文件中的指定文件夹路径下                zipFile.addFile(file, parameters);                log.info("文件已成功添加到zip文件中的指定文件夹。");            } catch (ZipException e) {                throw new RuntimeException(e);            }        }    }    return zipFile.getFile().getAbsolutePath();}

上面代码运行完毕,就会在d:/filename.zip的位置存在一个 zip 文件,打开后会看到目录结构和文件信息。接下来就需要前端来下载这个 zip 文件,我这里建立下载后就删除这个 zip 文件,这样下次重新生成时就不会出现文件合并的问题。后端下载代码如下:

/** * 下载文件的附件,附件较多 压缩为zip下载 * * @param ids      文件id集合 * @param response * @return */@Override@SneakyThrowspublic ResponseEntity<Resource> downloadZip(String ids, HttpServletResponse response) {    // 创建zip文件    String zipFilePath = createZipFile(ids);    File file = new File(zipFilePath);    InputStreamResource resource = new InputStreamResource(new FileInputStream(file));    return ResponseEntity.ok()            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=sample.zip")            .contentType(MediaType.APPLICATION_OCTET_STREAM)            .contentLength(file.length())            .body(resource);}

前段代码如下:

export function downloadCenterZip(ids) {    return request({        url: `/system/download/center/downloadZip`,        method: 'post',        data: ids,        responseType: 'blob'    // 标识返回类型为blob    })}
/** * 下载按钮操作 */function handleDownloadZip() {  downloadCenterZip(ids.value.toString()).then(res => {    console.log(res)    const url = window.URL.createObjectURL(new Blob([res], {type: 'application/zip'}));    let a = document.createElement('a')    a.href = url    a.download = 'download.zip'    a.click()    URL.revokeObjectURL(url) // 释放内存    a.remove()  })}

总结

对于其他格式文件的下载,其实思路都差不多,后端都需要返回一个blob或者byte数组,然后前端使用超链接的方式来触发下载。

发表评论

泰日号Copyright Your WebSite.Some Rights Reserved. 网站地图 备案号:川ICP备66666666号 Z-BlogPHP强力驱动