SpringBoot整合EasyExcel實現(xiàn)大規(guī)模數(shù)據(jù)的并行導出與壓縮下載
一、描述
在 Spring Boot 應用中,整合 EasyExcel 實現(xiàn)并行導出數(shù)據(jù)并進行 Zip 壓縮下載可以極大地提高數(shù)據(jù)處理效率和用戶體驗。以下是詳細描述及結合代碼的示例:
1、EasyExcel 簡介
EasyExcel 是一個 Java 操作 Excel 的開源工具,它能以簡單的方式讀寫大型 Excel 文件,并且性能高效、內(nèi)存占用低。
2、并行導出的優(yōu)勢
在處理大量數(shù)據(jù)導出時,傳統(tǒng)的單線程導出方式可能會非常耗時,導致用戶等待時間過長。而并行導出可以充分利用多核處理器的優(yōu)勢,將數(shù)據(jù)分成多個部分同時進行處理,從而大大提高導出速度。
3、Zip 壓縮下載的作用
當導出的數(shù)據(jù)量較大時,直接下載可能會導致網(wǎng)絡傳輸緩慢或者出現(xiàn)問題。通過將導出的 Excel 文件進行 Zip 壓縮,可以減小文件大小,提高下載速度,并且方便用戶管理和存儲。
二、案例
1、添加依賴
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> <!-- 簡化實體類的get,set操作 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> </dependency> <!-- Commons IO(用于壓縮文件操作) --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency>
2、準備數(shù)據(jù)模型
創(chuàng)建一個用于存儲數(shù)據(jù)的實體類:
@Data public class Order { private Long id; private String productName; private Integer quantity; private BigDecimal price; }
3、使用 EasyExcel 導出 Excel 文件
定義了一個名為ExcelExportUtil 的工具類,其中包含一個靜態(tài)方法writeToExcel。
這個方法的主要作用是將一個訂單列表(List<Order>)中的數(shù)據(jù)寫入到指定路徑的 Excel 文件中。它使用了阿里巴巴的 EasyExcel 庫來實現(xiàn)這個功能。
import com.alibaba.excel.EasyExcel; import java.io.File; import java.util.List; public class ExcelExportUtil { public static void writeToExcel(List<Order> orders, String filePath) { try { // 使用 EasyExcel 進行 Excel 文件寫入操作 // 指定輸出文件路徑 filePath、數(shù)據(jù)類型 Order.class 和工作表名稱 "訂單數(shù)據(jù)" EasyExcel.write(filePath, Order.class) .sheet("訂單數(shù)據(jù)") .doWrite(orders); } catch (Exception e) { // 如果在寫入過程中出現(xiàn)異常,打印異常信息 e.printStackTrace(); } } }
4、實現(xiàn)并行導出邏輯
import java.util.List; public interface ExportService { // 并行導出訂單的方法 void exportOrdersInParallel(List<List<Order>> ordersList, String outputDir); }
實現(xiàn)ExportService接口,這段代碼實現(xiàn)了并行導出訂單的功能。它使用了 Java 的CompletableFuture和自定義的線程池來同時處理多個訂單列表的導出任務
import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.IntStream; @Service public class ExportServiceImpl implements ExportService{ // 創(chuàng)建一個固定大小為 10 的線程池 private final ExecutorService executor = Executors.newFixedThreadPool(10); // 并行導出訂單的方法 public void exportOrdersInParallel(List<List<Order>> ordersList, String outputDir) { // 創(chuàng)建一個 CompletableFuture 數(shù)組來存儲每個任務的 Future 對象 CompletableFuture<Void>[] futures = new CompletableFuture[ordersList.size()]; // 遍歷訂單列表,為每個子列表創(chuàng)建一個異步任務 IntStream.range(0, ordersList.size()).forEach(index -> { List<Order> orders = ordersList.get(index); futures[index] = CompletableFuture.runAsync(() -> { // 生成文件名 String fileName = "訂單_" + Thread.currentThread().getId() + ".xlsx"; try { // 調(diào)用工具方法將訂單寫入 Excel 文件 ExcelExportUtil.writeOrdersToExcel(orders, outputDir + fileName); } catch (Exception e) { // 如果出現(xiàn)異常,打印異常信息 e.printStackTrace(); } }, executor); }); // 等待所有任務完成 CompletableFuture.allOf(futures).join(); } }
以下是對代碼的簡單解釋:
創(chuàng)建一個固定大小為 10 的線程池Executors,用于執(zhí)行異步任務。
exportOrdersInParallel方法接受一個訂單列表和輸出目錄作為參數(shù)。
創(chuàng)建一個CompletableFuture<Void>[]數(shù)組來存儲每個異步任務的 Future 對象。
使用IntStream.range遍歷訂單列表的索引,為每個訂單子列表創(chuàng)建一個異步任務。
在異步任務中,生成文件名,然后嘗試調(diào)用ExcelExportUtil.writeOrdersToExcel方法將訂單寫入 Excel 文件。如果出現(xiàn)異常,打印異常信息。
最后,使用CompletableFuture.allOf等待所有異步任務完成。
請注意,這段代碼假設存在一個Order
類和一個ExcelExportUtil
工具類,其中包含了將訂單寫入 Excel 文件的方法
5、壓縮文件為 zip
完成 Excel 文件的導出后,我們需要將這些文件壓縮成一個 zip 文件。
5.1 使用 ZipOutputStream
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipUtil { public static void zipFiles(String sourceDir, String zipFile) throws IOException { // 創(chuàng)建輸出的 ZIP 文件流 try (FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zipOut = new ZipOutputStream(fos)) { // 獲取要壓縮的源目錄下的文件列表 File fileToZip = new File(sourceDir); if (fileToZip.isDirectory()) { for (File file : fileToZip.listFiles()) { if (file.isFile()) { try (FileInputStream fis = new FileInputStream(file)) { // 創(chuàng)建 ZIP 條目,表示要添加到 ZIP 文件中的文件 ZipEntry zipEntry = new ZipEntry(file.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; // 循環(huán)讀取文件內(nèi)容并寫入 ZIP 文件 while ((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } } } } } } } }
以下是對代碼的詳細解釋:
zipFiles方法接受兩個參數(shù):sourceDir表示要壓縮的源目錄路徑,zipFile表示輸出的 ZIP 文件路徑。
在方法內(nèi)部,首先創(chuàng)建了一個FileOutputStream和一個ZipOutputStream,用于寫入 ZIP 文件。
然后獲取源目錄下的文件列表。如果源目錄是一個文件夾,則遍歷其中的文件。
對于每個文件,創(chuàng)建一個FileInputStream來讀取文件內(nèi)容。
創(chuàng)建一個ZipEntry,表示要添加到 ZIP 文件中的文件條目,條目名稱為文件的名稱。
將ZipEntry添加到ZipOutputStream中。
使用一個循環(huán),每次讀取 1024 字節(jié)的數(shù)據(jù),并將其寫入到 ZIP 文件中。
最后,關閉所有的輸入流和輸出流。
請注意,這段代碼假設源目錄中只包含文件,不包含子文件夾。如果需要遞歸壓縮子文件夾中的文件,可以對代碼進行進一步的擴展
6. 實現(xiàn)下載功能
在 Spring Boot 中,可以通過 HTTP 響應的形式將生成的 zip 文件提供給前端下載。
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FileDownloadController { @GetMapping("/downloadZip") public ResponseEntity<InputStreamResource> downloadZip() throws IOException { // 定義源目錄路徑,這里假設是 /tmp/excel_files String sourceDir = "/tmp/excel_files"; // 定義生成的 ZIP 文件路徑,這里假設是 /tmp/orders.zip String zipFilePath = "/tmp/orders.zip"; try { // 調(diào)用 ZipUtil 類的方法來壓縮源目錄中的文件到指定的 ZIP 文件 ZipUtil.zipFiles(sourceDir, zipFilePath); // 創(chuàng)建一個 File 對象表示要下載的 ZIP 文件 File file = new File(zipFilePath); // 創(chuàng)建一個 InputStreamResource 對象,從文件輸入流中讀取數(shù)據(jù) InputStreamResource resource = new InputStreamResource(new FileInputStream(file)); // 設置 HTTP 響應頭信息 HttpHeaders headers = new HttpHeaders(); // 設置內(nèi)容處置頭,指示瀏覽器下載文件,并指定文件名 headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName()); // 設置內(nèi)容類型為 application/octet-stream,表示二進制數(shù)據(jù) headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 設置內(nèi)容長度,以便瀏覽器正確顯示下載進度 headers.setContentLength(file.length()); // 返回一個包含 InputStreamResource 的 ResponseEntity 對象, // 表示成功的 HTTP 響應,包含要下載的文件數(shù)據(jù)和設置好的響應頭 return ResponseEntity.ok() .headers(headers) .body(resource); } catch (IOException e) { // 如果在壓縮文件或讀取文件過程中出現(xiàn) IOException,返回內(nèi)部服務器錯誤響應 return ResponseEntity.status(500).build(); } finally { // 在方法結束時,無論是否發(fā)生異常,嘗試刪除生成的 ZIP 文件 File zipFile = new File(zipFilePath); if (zipFile.exists()) { zipFile.delete(); } } } }
7. 完整的業(yè)務流程
數(shù)據(jù)分批處理:假設我們需要導出上百萬條訂單數(shù)據(jù),為了高效管理和處理,首先需要根據(jù)用戶ID、訂單日期或其他相關條件將龐大的數(shù)據(jù)集進行分片。每片數(shù)據(jù)將被獨立處理并導出到不同的 Excel 文件中,這樣可以有效減少單次處理的數(shù)據(jù)量,避免內(nèi)存溢出等問題。
并行處理:為了進一步提升導出效率,我們可以利用 Java 的
CompletableFuture
框架來并行處理各個數(shù)據(jù)片段。通過為每個數(shù)據(jù)片段分配一個獨立的導出任務,并讓這些任務在多個線程上同時執(zhí)行,可以確保多個 Excel 文件能夠同時生成,從而顯著加快整體處理速度。文件壓縮:在所有 Excel 文件成功生成后,我們需要將這些文件整合到一個壓縮包中以便于傳輸和存儲。這時,可以使用
ZipOutputStream
類來創(chuàng)建一個 zip 文件,并將所有生成的 Excel 文件逐一添加到這個 zip 文件中進行壓縮。這樣做不僅可以減少文件占用的空間,還能提高文件傳輸?shù)男省?/p>提供下載:為了讓用戶能夠方便地獲取壓縮后的數(shù)據(jù)包,我們需要在前端提供一個下載鏈接。當用戶點擊該鏈接時,服務器會將壓縮包發(fā)送給用戶的瀏覽器進行下載。為了確保下載過程的安全性和可靠性,可以采用 HTTPS 協(xié)議進行數(shù)據(jù)傳輸,并對下載鏈接進行時效性驗證和權限控制。
三、總結
本文介紹了使用Spring Boot和EasyExcel實現(xiàn)大規(guī)模數(shù)據(jù)高效導出的方法,通過數(shù)據(jù)分批處理、并行處理和文件壓縮等技術手段,提升了導出效率并優(yōu)化了用戶體驗,特別適用于需要處理大量數(shù)據(jù)的企業(yè)系統(tǒng)。
以上就是SpringBoot整合EasyExcel實現(xiàn)大規(guī)模數(shù)據(jù)的并行導出與壓縮下載的詳細內(nèi)容,更多關于SpringBoot EasyExcel數(shù)據(jù)導出和下載的資料請關注腳本之家其它相關文章!
相關文章
解決?IDEA?Maven?項目中"Could?not?find?artifact"?
這篇文章主要介紹了解決IDEA Maven項目中Could not?find?artifact問題的常見情況和解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07IDEA JarEditor編輯jar包方式(直接新增,修改,刪除jar包內(nèi)的class文件)
文章主要介紹了如何使用IDEA的JarEditor插件直接修改jar包內(nèi)的class文件,而不需要手動解壓、反編譯和重新打包,通過該插件,可以更方便地進行jar包的修改和測試2025-01-01SpringBoot接收form-data和x-www-form-urlencoded數(shù)據(jù)的方法
form-data和x-www-form-urlencoded是兩種不同的HTTP請求體格式,本文主要介紹了SpringBoot接收form-data和x-www-form-urlencoded數(shù)據(jù)的方法,具有一定的參考價值,感興趣的可以了解一下2024-05-05java list,set,map,數(shù)組間的相互轉(zhuǎn)換詳解
這篇文章主要介紹了java list,set,map,數(shù)組間的相互轉(zhuǎn)換詳解的相關資料,這里附有實例代碼,具有參考價值,需要的朋友可以參考下2017-01-01