最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于SpringBoot實(shí)現(xiàn)多文件批量下載并打包為ZIP壓縮包的完整解決方案

 更新時(shí)間:2026年02月03日 09:33:15   作者:qq_29757467  
在日常的 Web 開發(fā)中,文件下載是非常常見的功能需求,而多文件批量下載并打包為 ZIP 壓縮包 更是高頻場(chǎng)景(比如批量下載合同、圖片、報(bào)表等),本文將基于 SpringBoot 框架,手把手教你實(shí)現(xiàn)這一功能,從核心思路到完整代碼,讓你快速掌握,需要的朋友可以參考下

一、功能需求分析

我們要實(shí)現(xiàn)的核心功能:

  1. 前端傳入多個(gè)文件 ID,后端根據(jù) ID 查詢文件的存儲(chǔ)路徑
  2. 將這些文件讀取并打包成一個(gè) ZIP 壓縮包
  3. 通過 HTTP 響應(yīng)將 ZIP 包直接下載到客戶端
  4. 處理文件不存在、IO 異常等邊界情況

二、核心技術(shù)點(diǎn)

  • SpringBoot Web:提供 HTTP 接口、處理請(qǐng)求響應(yīng)
  • Java IO/NIO:讀取本地文件、操作輸出流
  • java.util.zip:JDK 原生 ZIP 壓縮工具類,無需引入額外依賴
  • 跨域處理:解決前后端分離架構(gòu)下的跨域問題
  • 日志記錄:記錄下載操作日志(可選)

三、代碼實(shí)現(xiàn)

3.1 實(shí)體類:Attachment(文件附件實(shí)體)

首先定義文件附件實(shí)體類,對(duì)應(yīng)數(shù)據(jù)庫中存儲(chǔ)的文件信息:

package com.itl.project.common.attachment.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.itl.framework.aspectj.lang.annotation.Excel;
import com.itl.framework.web.domain.BaseEntity;

import java.util.Date;

/**
 * 文件信息對(duì)象 sys_attachment
 *
 * @author itl
 */
@Data
public class Attachment extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 文件主鍵 */
    private Long fileId;

    /** 文件業(yè)務(wù)id */
    @Excel(name = "文件業(yè)務(wù)id")
    @TableField(exist = false)
    private String fileBusinessId;

    /** 文件key */
    @Excel(name = "文件key")
    private String fileKey;

    /** 文件名稱 */
    @Excel(name = "文件名稱")
    private String fileName;

    /** mime類型 */
    @Excel(name = "mime類型")
    private String mimeType;

    /** 文件大小 */
    @Excel(name = "文件大小")
    private Long fileSize;

    /** 文件路徑 */
    @Excel(name = "文件路徑")
    private String filePath;

    /** 文件地址 */
    @Excel(name = "文件地址")
    private String fileUrl;

    /** 文件后綴 */
    @Excel(name = "文件后綴")
    private String suffix;

    /** 是否存在 */
	private boolean exists = false;

    /** 創(chuàng)建時(shí)間 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;


    /**
     * 版本號(hào)
     */
    private String version;


    /**
     * 文件備注
     */
    private String comment;

    @TableField(exist = false)
    private String fileIds;

}

3.2 核心工具類:CompressDownloadUtil(ZIP 壓縮工具)

封裝 ZIP 壓縮的核心邏輯,負(fù)責(zé)將多個(gè)文件打包到輸出流中:

package com.itl.common.utils;

import com.itl.project.common.attachment.domain.Attachment;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件壓縮下載工具類
 * 功能:將多個(gè)文件壓縮到指定輸出流中,用于ZIP包下載
 */
public class CompressDownloadUtil {

    /**
     * 將多個(gè)文件壓縮到指定輸出流中
     *
     * @param lists 需要壓縮的文件列表
     * @param outputStream  壓縮后的輸出流(直接關(guān)聯(lián)HTTP響應(yīng)輸出流)
     */
    public static void compressZip(List<Attachment> lists, OutputStream outputStream) {
        ZipOutputStream zipOutStream = null;
        try {
            // 包裝成ZIP格式輸出流,提升寫入效率
            zipOutStream = new ZipOutputStream(new BufferedOutputStream(outputStream));
            // 設(shè)置壓縮方法為DEFLATED(默認(rèn),有壓縮效果)
            zipOutStream.setMethod(ZipOutputStream.DEFLATED);
            
            // 循環(huán)處理每個(gè)文件
            for (Attachment file : lists) {
                java.io.File localFile = new java.io.File(file.getFilePath());
                // 校驗(yàn)文件是否存在,避免空指針或文件找不到異常
                if (localFile.exists() && localFile.isFile()) {
                    try (FileInputStream fileInputStream = new FileInputStream(localFile)) {
                        // 讀取文件字節(jié)數(shù)據(jù)
                        byte[] data = new byte[(int) localFile.length()];
                        fileInputStream.read(data);
                        
                        // 創(chuàng)建ZIP條目(壓縮包內(nèi)的文件名稱)
                        ZipEntry zipEntry = new ZipEntry(file.getFileName());
                        zipEntry.setSize(localFile.length());
                        
                        // 將條目寫入ZIP流
                        zipOutStream.putNextEntry(zipEntry);
                        zipOutStream.write(data);
                        // 關(guān)閉當(dāng)前條目
                        zipOutStream.closeEntry();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("文件壓縮失敗:" + e.getMessage());
        } finally {
            // 關(guān)閉流(注意關(guān)閉順序:先關(guān)ZIP流,再關(guān)基礎(chǔ)輸出流)
            try {
                if (Objects.nonNull(zipOutStream)) {
                    zipOutStream.flush();
                    zipOutStream.close();
                }
                if (Objects.nonNull(outputStream)) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.3 控制器:文件下載接口

import com.itl.common.utils.CompressDownloadUtil;
import com.itl.project.common.attachment.domain.Attachment;
import com.itl.project.common.attachment.service.AttachmentService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.UUID;

/**
 * 文件下載控制器
 */
@RestController
@RequestMapping("/file")
public class FileDownloadController {

    // 注入文件附件服務(wù)(實(shí)際項(xiàng)目中通過@Autowired注入)
    private AttachmentService attachmentService;

    /**
     * 多文件下載為ZIP壓縮包
     * @param fileid 多個(gè)文件ID,建議用逗號(hào)分隔(如:1,2,3)
     * @param request HTTP請(qǐng)求
     * @param response HTTP響應(yīng)
     * @throws UnsupportedEncodingException 編碼異常
     */
    @Log(title = "多文件下載", businessType = BusinessType.DOWNLOAD)
    @CrossOrigin // 解決跨域問題(前后端分離必加)
    @GetMapping("/downloadZip/{fileid}")
    public void downloadZip(@PathVariable("fileid") String fileid, 
                           HttpServletRequest request,
                           HttpServletResponse response) throws UnsupportedEncodingException {
        // 1. 根據(jù)文件ID查詢文件列表
        Attachment attachment = new Attachment();
        attachment.setFileIds(fileid);
        List<Attachment> attachmentList = attachmentService.selectAttachmentList(attachment);
        
        // 2. 校驗(yàn)文件列表是否為空
        if (attachmentList.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        
        // 3. 設(shè)置響應(yīng)頭,告訴瀏覽器下載文件
        // 生成唯一的ZIP包名稱(避免重名)
        String downloadName = UUID.randomUUID().toString().replaceAll("-", "") + ".zip";
        // 解決文件名中文亂碼問題
        String fileName = new String(downloadName.getBytes("UTF-8"), "ISO8859-1");
        
        // 設(shè)置響應(yīng)頭
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        // 設(shè)置響應(yīng)內(nèi)容類型為二進(jìn)制流(通用文件下載類型)
        response.setContentType("application/octet-stream");
        // 禁用緩存
        response.setHeader("Cache-Control", "no-cache");
        
        // 4. 調(diào)用工具類壓縮文件并寫入響應(yīng)輸出流
        try {
            CompressDownloadUtil.compressZip(attachmentList, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

四、關(guān)鍵細(xì)節(jié)說明

4.1 解決文件名中文亂碼

String fileName = new String(downloadName.getBytes("UTF-8"), "ISO8859-1");

4.2 流的關(guān)閉順序

在工具類中,關(guān)閉流的順序必須是:先關(guān)閉 ZipOutputStream,再關(guān)閉基礎(chǔ)的 OutputStream。因?yàn)?ZipOutputStream 是包裝流,關(guān)閉包裝流時(shí)會(huì)自動(dòng)刷新緩沖區(qū),確保所有數(shù)據(jù)都寫入基礎(chǔ)流。

4.3 異常處理

  • 校驗(yàn)文件是否存在:避免讀取不存在的文件導(dǎo)致 IO 異常
  • 響應(yīng)狀態(tài)碼:文件不存在返回 404,服務(wù)器異常返回 500,前端可根據(jù)狀態(tài)碼給出友好提示
  • try-with-resources:在讀取文件時(shí)使用try (FileInputStream fileInputStream = new FileInputStream(localFile)),自動(dòng)關(guān)閉流,簡(jiǎn)化代碼

4.4 跨域處理

  • @CrossOrigin注解解決前后端分離架構(gòu)下的跨域問題,如果是全局跨域配置,可省略該注解。

五、前端調(diào)用示例(Axios)

// 前端下載ZIP包示例
function downloadZip(fileIds) {
    // 創(chuàng)建a標(biāo)簽,通過GET請(qǐng)求下載
    const a = document.createElement('a');
    a.href = `/file/downloadZip/${fileIds}`;
    a.download = '文件包.zip'; // 可選,優(yōu)先使用后端返回的文件名
    a.click();
    // 移除a標(biāo)簽
    a.remove();
}

// 調(diào)用示例:下載ID為1,2,3的文件
downloadZip("1,2,3");

六、優(yōu)化建議

  1. 大文件處理:本文代碼適用于中小文件,若處理大文件,建議使用BufferedInputStream分塊讀取,避免一次性加載文件到內(nèi)存導(dǎo)致 OOM。
  2. 文件名去重:如果壓縮包內(nèi)有重名文件,可在文件名后添加序號(hào)(如:test.txt → test (1).txt)。
  3. 進(jìn)度條:大文件下載時(shí),可結(jié)合 WebSocket 實(shí)現(xiàn)下載進(jìn)度條。
  4. 權(quán)限控制:在接口中添加權(quán)限校驗(yàn),確保只有授權(quán)用戶才能下載文件。
  5. 日志完善:除了操作日志,可記錄文件下載的大小、耗時(shí)、用戶等信息,便于問題排查。

七、總結(jié)

本文基于 SpringBoot + JDK 原生 ZIP 工具類,實(shí)現(xiàn)了多文件批量下載并打包為 ZIP 壓縮包的功能,核心思路是:
查詢文件列表 → 2. 設(shè)置下載響應(yīng)頭 → 3. 讀取文件并壓縮 → 4. 寫入響應(yīng)輸出流。
該方案無需引入額外的壓縮依賴,基于 JDK 原生 API 實(shí)現(xiàn),輕量且穩(wěn)定,適用于大多數(shù) Web 項(xiàng)目的文件下載場(chǎng)景。如果有更復(fù)雜的壓縮需求(如加密、分卷壓縮),可考慮使用 Apache Commons Compress 工具包。

以上就是基于SpringBoot實(shí)現(xiàn)多文件批量下載并打包為ZIP壓縮包的完整解決方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot多文件下載并打包為ZIP壓縮包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Cloud入門系列服務(wù)提供者總結(jié)

    Spring Cloud入門系列服務(wù)提供者總結(jié)

    這篇文章主要介紹了Spring Cloud入門系列之服務(wù)提供者總結(jié),服務(wù)提供者使用Eureka Client組件創(chuàng)建 ,創(chuàng)建完成以后修改某文件,具體操作方法及實(shí)例代碼跟隨小編一起看看吧
    2021-06-06
  • SpringBoot全局異常處理之解決404/500錯(cuò)誤

    SpringBoot全局異常處理之解決404/500錯(cuò)誤

    在搭建項(xiàng)目框架的時(shí)候用的是springboot,想統(tǒng)一處理異常,但是發(fā)現(xiàn)404的錯(cuò)誤總是捕捉不到,總是返回的是springBoot自帶的錯(cuò)誤結(jié)果信息,這篇文章主要給大家介紹了關(guān)于SpringBoot全局異常處理之解決404/500錯(cuò)誤的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • SpringBoot因?yàn)閖ackson版本問題啟動(dòng)失敗的解決

    SpringBoot因?yàn)閖ackson版本問題啟動(dòng)失敗的解決

    SpringBoot啟動(dòng)失敗解決Jackson版本沖突,通過調(diào)整pom.xml中的Jackson依賴版本,確保與SpringBoot兼容,避免啟動(dòng)時(shí)的嵌入容器初始化異常
    2026-05-05
  • 詳解java中通過post方式訪問后臺(tái)服務(wù)器

    詳解java中通過post方式訪問后臺(tái)服務(wù)器

    本篇文章主要介紹了詳解java中通過post方式訪問后臺(tái)服務(wù)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Java文件讀取寫入后 md5值不變的實(shí)現(xiàn)方法

    Java文件讀取寫入后 md5值不變的實(shí)現(xiàn)方法

    下面小編就為大家分享一篇Java文件讀取寫入后 md5值不變的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助
    2017-11-11
  • SpringCloud Config使用本地倉庫及map注入

    SpringCloud Config使用本地倉庫及map注入

    這篇文章主要介紹了SpringCloud Config使用本地倉庫及map注入,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • SpringBoot定時(shí)任務(wù)@Scheduled使用方式

    SpringBoot定時(shí)任務(wù)@Scheduled使用方式

    本文主要介紹了SpringBoot中的定時(shí)任務(wù)的實(shí)現(xiàn)方法,通過@EnableScheduling開啟定時(shí)任務(wù),使用@Scheduled注解可以設(shè)置定時(shí)任務(wù)的觸發(fā)方式,文中詳細(xì)介紹了corn參數(shù)的使用方法和各種通配符的含義,并給出了示例代碼
    2026-04-04
  • java源碼解析之String類的compareTo(String otherString)方法

    java源碼解析之String類的compareTo(String otherString)方法

    這篇文章主要給大家介紹了關(guān)于java源碼解析之String類的compareTo(String otherString)方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • 實(shí)現(xiàn)quartz定時(shí)器及quartz定時(shí)器原理介紹

    實(shí)現(xiàn)quartz定時(shí)器及quartz定時(shí)器原理介紹

    Quartz是一個(gè)大名鼎鼎的Java版開源定時(shí)調(diào)度器,功能強(qiáng)悍,使用方便,下面我們看看如何使用它
    2013-12-12
  • Spring-全面詳解(學(xué)習(xí)總結(jié))

    Spring-全面詳解(學(xué)習(xí)總結(jié))

    這篇文章主要介紹了詳解Spring框架入門,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望能給你帶來幫助
    2021-07-07

最新評(píng)論

宣恩县| 礼泉县| 宁国市| 绍兴县| 乌兰察布市| 东海县| 漯河市| 绥江县| 石门县| 长兴县| 巴彦淖尔市| 柳河县| 临城县| 林口县| 鄂温| 甘孜县| 长武县| 义马市| 广西| 阳朔县| 丰原市| 大理市| 桐柏县| 六盘水市| 曲水县| 东兰县| 上栗县| 曲麻莱县| 林芝县| 宝丰县| 乌拉特前旗| 石首市| 凤翔县| 华阴市| 乌苏市| 晋江市| 贺州市| 云南省| 毕节市| 万州区| 财经|