SpringBoot根據多階層創建文件,然後壓縮成壓縮包進行下載

来源:https://www.cnblogs.com/Serendipitys/p/17979459
-Advertisement-
Play Games

臨時接到一個需求說讓根據按照下麵的這個圖片的結構來打包下載指定位置下的文件到指定位置! 實現思路: 1.把已經實現的樹形結構的代碼進行調用,拿到他的數據進行創建對應的文件夾 2.因為結構下方的文件沒有特別直觀的資料庫中的關聯關係,所以還需要對於管理關係進行梳理 3.創建好階級文件,然後調用網上找的工 ...


臨時接到一個需求說讓根據按照下麵的這個圖片的結構來打包下載指定位置下的文件到指定位置!

實現思路:
  1.把已經實現的樹形結構的代碼進行調用,拿到他的數據進行創建對應的文件夾
  2.因為結構下方的文件沒有特別直觀的資料庫中的關聯關係,所以還需要對於管理關係進行梳理
  3.創建好階級文件,然後調用網上找的工具類打包成為rar壓縮包,然後把路勁交給前端進行調用下載
調用數據,然後傳遞給創建文件方法進行實現:
/**
 * 打包佐證成果文件,壓縮成為壓縮包!
 *
 * @param projectId
 * @param departId
 */
@ApiOperation(value = "打包佐證成果文件", notes = "佐證與成果-打包佐證成果文件")
@RequestMapping("/exportZip")
public Result<?> exportZip(@RequestParam(name = "projectId", required = false) String projectId, @RequestParam(name = "departId", required = true) String departId) {
    // 獲取樹形結構
    Result<List<SelectTreeMoneyModel>> loadAllTreeRoot = this.loadAllTreeRoot(projectId, departId);
    // 下載壓縮文件,將獲取到的樹形結構,傳遞到實現類進行解析跟實現
    String downloadZipFile = downloadZipFile(loadAllTreeRoot, "佐證跟成果", "/");
    return Result.ok(downloadZipFile);
}
遞歸的創建子集文件夾,然後調用工具類進行壓縮成為壓縮包文件,註:刪除文件必須捋清楚然後進行使用,其實不刪除也只會在指定的位置生成一份,所以我這邊沒有進行使用!
 /**
 * 遞歸的創建子集文件夾
 *
 * @param data
 * @param rootFile
 */

private void mkdirsChild(List<SelectTreeMoneyModel> data, File rootFile) {
    for (SelectTreeMoneyModel datum : data) {
        // 創建一個file實例對象,指向文件路徑(存放照片的根目錄)
        File childs = new File(rootFile, datum.getTitle());
        if (!childs.exists()) {
            childs.mkdirs();
        }
        // 判斷如果下麵還有子節點,如果有則調用自身
        if (!datum.isLeaf()) {
            mkdirsChild(datum.getChildren(), childs);
        }
        // 如果下麵沒有子節點,則進行判斷下麵是否有附件,如果有附件則進行下載到指定的文件夾內。
        List<ProjectResult> results = iProjectResultService.list(new LambdaQueryWrapper<ProjectResult>().eq(ProjectResult::getTypeId, datum.getKey()));
        if (ObjectUtils.isNotEmpty(results)) {
            for (ProjectResult result : results) {
                List<ProjectTaskContent> projectTaskContents = projectTaskContentService.list(new LambdaQueryWrapper<ProjectTaskContent>().eq(ProjectTaskContent::getResultId, result.getId()));
                for (ProjectTaskContent projectTaskContent : projectTaskContents) {
                    // 判斷附件表不是空的,則進行下載文件到對應的文件夾下
                    if (ObjectUtils.isNotEmpty(projectTaskContents)) {
                        RestTemplate restTemplate = new RestTemplate();
                        // 配置文件進行讀取
                        try {
                            ResponseEntity responseEntity = restTemplate.exchange(url + projectTaskContent.getFilePath(), HttpMethod.GET, null, byte[].class);
                            byte[] fileContent = (byte[]) responseEntity.getBody();
                            // 利用 File 對象,然後使用 getName() 方法獲取文件名(不包括路徑)。
                            File file = new File(projectTaskContent.getName());
                            String filenameWithoutPrefix = file.getName();
                            Files.write(Paths.get(childs + "\\" + filenameWithoutPrefix), fileContent);
                        } catch (IOException e) {
                            e.getMessage();
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }
}
 /**
 * 下載壓縮文件
 *
 * @param data       數據集合【key:分類名稱,value:照片信息集合(key:照片名稱,value:照片下載路徑)】
 * @param fileStr    照片存放的文件路徑
 * @param zipFileStr 壓縮文件的路徑(加尾碼名)
 */
public String downloadZipFile(Result<List<SelectTreeMoneyModel>> data, String fileStr, String zipFileStr) {
    File rootFile = null;
    String folderPath = null;
    try {
        // 遍歷傳遞進來的數據,然後根據傳入的數據進行創建文件夾
        for (SelectTreeMoneyModel selectTreeMoneyModel : data.getResult()) {
            // 創建一個file實例對象,指向文件路徑(存放照片的根目錄)
            zipFileStr = folderUri + selectTreeMoneyModel.getTitle();
            folderPath = selectTreeMoneyModel.getTitle();
            rootFile = new File(zipFileUri + selectTreeMoneyModel.getTitle());
            if (!rootFile.exists()) {
                // 創建新文件夾,可以多層(mkdir()創建新文件夾,只能創建一層)
                rootFile.mkdirs();
            }
            // 根據判斷遞歸的創建文件夾,如果是false則有子集
            if (!selectTreeMoneyModel.isLeaf()) {
                mkdirsChild(selectTreeMoneyModel.getChildren(), rootFile);
            }
        }
        // 創建文件輸出流(zip流對象)【實際創建了zip文件,0kb】
        FileOutputStream fos1 = new FileOutputStream(new File(zipFileStr + ".zip"));
        // 壓縮法
        toZip1(rootFile, fos1, true);
        //TODO  刪除文件和壓縮文件,要保證每次壓縮只保存一份最新的存在。 因為是刪除文件,所以要慎用
        //delFolder(folderUri + folderPath);
        //delFolder(zipFileStr);
    } catch (IOException e) {
        e.printStackTrace();
    }
    // 拼接返回的壓縮包地址
    String urlResult = url + folderPath + ".zip";
    return urlResult;
}

/**
 * 刪除文件夾
 *
 * @param folderPath 文件夾完整絕對路徑
 */
public static void delFolder(String folderPath) {
    try {
        // 刪除目錄下所有內容
        delAllFile(folderPath);
        File myFilePath = new File(folderPath);
        //刪除空文件夾
        myFilePath.delete();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * 刪除指定文件夾下所有文件
 *
 * @param path 文件夾完整絕對路徑
 */
public static boolean delAllFile(String path) {
    boolean bea = false;
    File file = new File(path);
    if (!file.exists()) {
        return bea;
    }
    if (!file.isDirectory()) {
        return bea;
    }
    //
    String[] tempList = file.list();
    File temp;
    if (tempList != null) {
        for (String var : tempList) {
            // separator 代替文件或文件夾路徑的斜線或反斜線,防止跨平臺出現錯誤
            if (path.endsWith(File.separator)) {
                temp = new File(path + var);
            } else {
                temp = new File(path + File.separator + var);
            }
            if (temp.isFile()) {
                temp.delete();
            }
            if (temp.isDirectory()) {
                //先刪除文件夾裡面的文件
                delAllFile(path + "/" + var);
                //再刪除空文件夾
                delFolder(path + "/" + var);
                bea = true;
            }
        }
    }
    return bea;
}

/**
 * 壓縮的遞歸方法
 *
 * @param sourceFile       源文件
 * @param zos              zip輸出流
 * @param fileName         源文件的名稱
 * @param keepDirStructure 是否保留原來的目錄結構,true:保留目錄結構;
 *                         false:所有文件跑到壓縮包根目錄下(註意:不保留目錄結構可能會出現同名文件,會壓縮失敗)
 */
private void compress(File sourceFile, ZipOutputStream zos, String fileName, boolean keepDirStructure) throws IOException {
    byte[] buf = new byte[2 * 1024];
    // 判斷是否是一個文件
    if (sourceFile.isFile()) {
        // 向zip輸出流中添加一個zip實體,構造器中name為zip實體的文件的名字
        zos.putNextEntry(new ZipEntry(fileName));
        // 創建文件(即某張圖片)的輸入流
        try (FileInputStream in = new FileInputStream(sourceFile)) {
            int len;
            // read方法:每調用一次就從FileInputStream流中讀取一個位元組,並返回下一個數據位元組,若已到達末尾,就返回-1。
            while ((len = in.read(buf, 0, buf.length)) != -1) {
                zos.write(buf, 0, len);
            }
            // 實際寫入到了zip輸出流的zip實體中,還沒寫到文件中【zip文件時0kb,不能打開,因為流沒有關閉】
            zos.closeEntry();
        } catch (IOException e) {
            throw new IOException(e);
        }
    } else {
        // 源文件時目錄
        // 獲取該目錄下所有文件和目錄的絕對路徑
        File[] listFiles = sourceFile.listFiles();
        // 空目錄
        if (listFiles == null || listFiles.length == 0) {
            // 需要保留原來的文件結構時,需要對空文件夾進行處理
            if (keepDirStructure) {
                // 空文件夾的處理
                zos.putNextEntry(new ZipEntry(fileName + "/"));
                // 沒有文件,不需要文件的copy
                zos.closeEntry();
            }
        } else {
            // 非空目錄
            for (File file : listFiles) {
                if (keepDirStructure) {
                    // 註意:getName()僅得到最後一層的名字,不是路徑,所以要加“/”,不然所有文件都跑到壓縮包根目錄下了
                    compress(file, zos, fileName + "/" + file.getName(), true);
                } else {
                    compress(file, zos, file.getName(), false);
                }
            }
        }
    }
}

/**
 * 壓縮成ZIP 方法1:保留多級目錄結構
 *
 * @param sourceFile       照片存放路徑
 * @param out              壓縮文件輸出流
 * @param keepDirStructure 是否保留原來的目錄結構,true:保留目錄結構;
 *                         false:所有文件跑到壓縮包根目錄下(註意:不保留目錄結構可能會出現同名文件,會壓縮失敗)
 */
public void toZip1(File sourceFile, OutputStream out, boolean keepDirStructure) throws IOException {
    long start = System.currentTimeMillis();
    // 創建壓縮輸出流,java7的新語法:Try-with-resources,會確保異常拋出或者try代碼塊結束時close流【該流必須實現AutoCloseable類】(若是java7之前的版本,則不會生效)
    try (ZipOutputStream zos = new ZipOutputStream(out)) {
        // 壓縮
        compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
        long end = System.currentTimeMillis();
        System.out.println("壓縮完成,耗時:" + (end - start) + " ms");
    } catch (IOException e) {
        throw new IOException(e);
    }
}
最後的實現結果

      總結: 主要還是要理清楚你的層級關係,
      1.然後在遞歸的時候一定要遞歸到最底層,然後根據最底層的數據查找跟附件表有關係的ID進行查找,然後將有關係的文件下載到指定的文件夾下,
      2.然後打包成為壓縮包這種實現直接可以進行百度查找到適合自己的工具類,如果不是直接適用,可以進行修改工具類的方法進行適配。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 依賴管理解決了在軟體開發過程中管理和協調各種依賴項的問題,簡化了開發流程,提高了項目的可靠性、可維護性和可重覆性。它們幫助開發人員更高效地管理項目的依賴關係,減少了潛在的錯誤和衝突,並提供了更好的開發體驗。 常用的依賴管理 在 JavaS ...
  • 最近,有群里在群里發了這麼一個非常有意思的卡片 Hover 動效,來源於此網站 -- key-drop,效果如下: 非常有意思酷炫的效果。而本文,我們不會完全還原此效果,而是基於此效果,嘗試去製作這麼一個類似的卡片交互效果: 該效果的幾個核心點: 卡片的 3D 旋轉跟隨滑鼠移動效果 如何讓卡片在 H ...
  • 一、定義 定義一個操作中演算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類不改變一個演算法的結構即可重定義該演算法的特定步驟。模板方法是一種類行為型模式 二、描述 模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計,包含以下兩個角色: 1、AbstractClass(抽象類):在抽象 ...
  • 本文在原文基礎上有刪減,原文參考泛型、Trait 和生命周期。 目錄泛型數據類型在函數定義中使用泛型結構體定義中的泛型枚舉定義中的泛型方法定義中的泛型泛型代碼的性能Trait:定義共同行為定義 trait為類型實現 trait預設實現trait 作為參數Trait Bound 語法通過 + 指定多個 ...
  • 一、pom.xml需要引入的依賴二、項目開啟熔斷器開關 2.1 註解方式 2.2 xml方式三、依賴類缺失問題四、版本匹配安全檢查問題五、測試驗證六、結論 一、pom.xml需要引入的依賴 pom.xml <!-- springboot升級到2.6.7,同樣適用於2.7.0,2.7.18等 --> ...
  • 看到標題大家可能會有點疑惑吧:OpenFeign 不是挺好用的嗎?尤其是微服務之間的遠程調用,平時用的也挺習慣的,為啥要替換呢? ...
  • 當然,我寫的簡易版協程池還有很多可以優化的地方,比如可以實現動態擴容等功能。今天我們要簡單總結一下協程池的優勢,主要是為了降低資源開銷。協程池的好處在於可以重覆利用協程,避免頻繁創建和銷毀協程,從而減少系統開銷,提高系統性能。此外,協程池還可以提高響應速度,因為一旦接收到任務,可以立即執行,不需要等... ...
  • ZooKeeperServer 實現了單機版zookeeper服務端功能,子類實現了更加豐富的分散式集群功能: ZooKeeperServer |-- QuorumZooKeeperServer |-- LeaderZooKeeperServer |-- LearnerZooKeeperServer ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...