1. 初始化Spring Boot項目
首先,使用Spring Initializr(https://start.spring.io/)生成一個基本的Spring Boot項目。選擇以下依賴項:
- Spring Web
- Lombok (用于減少樣板代碼)
- SLF4J (用于日志記錄)
2. 添加依賴
在你的pom.xml
文件中添加EasyExcel的Maven依賴。確保版本號是最新的,你可以訪問Maven倉庫來獲取最新版。
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>最新的版本號</version>
</dependency>
3. 創建實體類
假設我們需要處理一個用戶信息表,包含姓名和年齡兩個字段。以下是實體類的設計,并使用Lombok簡化代碼:
package com.example.demo.model;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;// 使用@Data注解自動生成getter、setter等方法
@Data
public class UserData {// 指定Excel列標題為“姓名”@ExcelProperty("姓名")private String name; // 用戶姓名// 指定Excel列標題為“年齡”@ExcelProperty("年齡")private Integer age; // 用戶年齡
}
4. 創建監聽器類
創建一個監聽器類來處理每一行的數據,并在服務類中調用它。我們使用@Slf4j
注解簡化日志記錄:
package com.example.demo.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;@Slf4j // 使用@Slf4j注解簡化日志記錄
public class UserDataListener extends AnalysisEventListener<UserData> {private final List<UserData> dataList = new ArrayList<>(); // 存儲讀取的數據/*** 當解析一行數據時調用* @param userData 解析得到的用戶數據* @param analysisContext 上下文對象*/@Overridepublic void invoke(UserData userData, AnalysisContext analysisContext) {dataList.add(userData); // 將每一行的數據添加到列表中log.info("讀取到一條數據: {} {}", userData.getName(), userData.getAge()); // 日志記錄}/*** 所有數據解析完成后調用* @param analysisContext 上下文對象*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {log.info("所有數據解析完成"); // 數據解析完成后記錄日志}/*** 獲取讀取的數據列表* @return 讀取的數據列表*/public List<UserData> getDataList() {return dataList; // 返回讀取的數據列表}
}
5. 實現服務類
編寫一個服務類來實現數據的讀寫操作,并增加異常處理和日志記錄。我們將在此處添加分頁功能,以確保當單個頁面數據達到一定量時重新生成新的頁面進行寫入:
package com.example.demo.service;import com.alibaba.excel.EasyExcel;
import com.example.demo.listener.UserDataListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Slf4j // 使用@Slf4j注解簡化日志記錄
@Service
public class ExcelService {private static final int PAGE_SIZE = 100; // 每頁最大數據條數/*** 寫入Excel文件* @param filePath 文件路徑* @param data 要寫入的數據列表*/public void writeExcel(String filePath, List<UserData> data) {try {log.info("開始寫入Excel文件: {}", filePath); // 記錄日志int totalRows = data.size();int totalPages = (int) Math.ceil((double) totalRows / PAGE_SIZE);for (int page = 0; page < totalPages; page++) {int fromIndex = page * PAGE_SIZE;int toIndex = Math.min(fromIndex + PAGE_SIZE, totalRows);List<UserData> currentPageData = data.subList(fromIndex, toIndex);String sheetName = "用戶信息" + (page + 1); // 設置工作表名稱// 開始寫入Excel文件EasyExcel.write(filePath, UserData.class).sheet(sheetName) // 設置工作表名稱.doWrite(currentPageData); // 執行寫入操作log.info("寫入第 {} 頁數據完成", page + 1); // 寫入完成后記錄日志}log.info("寫入Excel文件完成"); // 寫入完成后記錄日志} catch (Exception e) {log.error("寫入Excel失敗", e); // 記錄錯誤日志}}/*** 讀取所有工作表的Excel文件* @param inputStream 輸入流* @return 讀取的數據列表*/public List<UserData> readAllSheets(InputStream inputStream) {List<UserData> allData = new ArrayList<>();try {log.info("開始讀取所有工作表的Excel文件"); // 記錄日志// 獲取Excel文件中的所有Sheet信息List<String> sheetNames = EasyExcel.read(inputStream).excelExecutor().sheetList().get();for (String sheetName : sheetNames) {log.info("開始讀取工作表: {}", sheetName);UserDataListener listener = new UserDataListener();// 執行讀取操作EasyExcel.read(inputStream, UserData.class, listener).sheet(sheetName) // 指定工作表名稱.doRead(); // 執行讀取操作// 處理listener.getDataList()allData.addAll(listener.getDataList());log.info("讀取工作表 {} 完成", sheetName);}log.info("讀取所有工作表的Excel文件完成"); // 讀取完成后記錄日志} catch (Exception e) {log.error("讀取所有工作表的Excel失敗", e); // 記錄錯誤日志}return allData;}/*** 讀取特定工作表的Excel文件* @param filePath 文件路徑* @param sheetIndex 工作表索引(從0開始)*/public void readSpecificSheet(String filePath, int sheetIndex) {try {log.info("開始讀取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 記錄日志UserDataListener listener = new UserDataListener();// 執行讀取操作EasyExcel.read(filePath, UserData.class, listener).sheet(sheetIndex) // 指定工作表索引.doRead(); // 執行讀取操作// 處理listener.getDataList()for (UserData userData : listener.getDataList()) {log.info("{} {}", userData.getName(), userData.getAge()); // 記錄每條數據的日志}log.info("讀取特定工作表的Excel文件完成"); // 讀取完成后記錄日志} catch (Exception e) {log.error("讀取特定工作表的Excel失敗", e); // 記錄錯誤日志}}/*** 使用模板填充數據并生成新的Excel文件* @param templateFilePath 模板文件路徑* @param outputFilePath 輸出文件路徑* @param data 要填充的數據列表*/public void fillTemplate(String templateFilePath, String outputFilePath, List<UserData> data) {try {log.info("開始使用模板填充數據: {}, 輸出文件路徑: {}", templateFilePath, outputFilePath); // 記錄日志// 使用模板填充數據EasyExcel.write(outputFilePath).withTemplate(templateFilePath).sheet().doFill(data);log.info("模板填充數據完成"); // 填充完成后記錄日志} catch (Exception e) {log.error("模板填充數據失敗", e); // 記錄錯誤日志}}/*** 下載文件并在失敗時返回JSON* @param response HttpServletResponse 對象* @throws IOException 如果寫入文件失敗*/public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {try {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 這里URLEncoder.encode可以防止中文亂碼String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");List<UserData> data = new ArrayList<>();for (int i = 1; i <= 100; i++) { // 假設我們有100條數據data.add(new UserData().setName("張三" + i).setAge(20 + i % 50));}EasyExcel.write(response.getOutputStream(), UserData.class).autoCloseStream(Boolean.FALSE).sheet("模板").doWrite(data);} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");Map<String, String> map = Map.of("status", "failure", "message", "下載文件失敗: " + e.getMessage());response.getWriter().println(JSON.toJSONString(map));}}
}
}
6. 創建控制器類
為了方便測試,我們可以創建一個簡單的控制器類來調用服務類中的方法:
package com.example.demo.controller;import com.example.demo.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@Slf4j // 使用@Slf4j注解簡化日志記錄
@RestController
@RequestMapping("/excel")
public class ExcelController {@Autowiredprivate ExcelService excelService; // 自動注入ExcelService/*** 文件上傳* @param file 上傳的文件* @return 成功消息* @throws IOException 如果讀取文件失敗*/@PostMapping("upload")@ResponseBodypublic String upload(@RequestParam("file") MultipartFile file) throws IOException {InputStream inputStream = file.getInputStream();List<UserData> data = excelService.readAllSheets(inputStream);log.info("成功讀取到 {} 條數據", data.size());return "success";}/*** 文件下載并且失敗的時候返回json(默認失敗了會返回一個有部分數據的Excel)** @param response HttpServletResponse 對象* @throws IOException 如果寫入文件失敗*/@GetMapping("downloadFailedUsingJson")public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {excelService.downloadFailedUsingJson(response);}/*** 寫入Excel文件* @param filePath 文件路徑* @return 成功消息*/@GetMapping("/write")public String writeExcel(@RequestParam String filePath) {log.info("準備寫入Excel文件: {}", filePath); // 記錄日志List<UserData> data = new ArrayList<>();for (int i = 1; i <= 500; i++) { // 假設我們有500條數據data.add(new UserData().setName("張三" + i).setAge(20 + i % 50));}excelService.writeExcel(filePath, data); // 調用寫入方法log.info("寫入Excel文件完成"); // 返回成功消息return "寫入Excel成功"; // 返回成功消息}/*** 讀取所有工作表的Excel文件* @param filePath 文件路徑* @return 成功消息*/@GetMapping("/read/allSheets")public String readAllSheets(@RequestParam String filePath) {log.info("準備讀取所有工作表的Excel文件: {}", filePath); // 記錄日志excelService.readAllSheets(filePath); // 調用讀取方法log.info("讀取所有工作表的Excel文件完成"); // 返回成功消息return "讀取所有工作表的Excel成功"; // 返回成功消息}/*** 讀取特定工作表的Excel文件* @param filePath 文件路徑* @param sheetIndex 工作表索引(從0開始)* @return 成功消息*/@GetMapping("/read/specificSheet")public String readSpecificSheet(@RequestParam String filePath, @RequestParam int sheetIndex) {log.info("準備讀取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 記錄日志excelService.readSpecificSheet(filePath, sheetIndex); // 調用讀取方法log.info("讀取特定工作表的Excel文件完成"); // 返回成功消息return "讀取特定工作表的Excel成功"; // 返回成功消息}/*** 使用模板填充數據并生成新的Excel文件* @param templateFilePath 模板文件路徑* @param outputFilePath 輸出文件路徑* @return 成功消息*/@GetMapping("/fill/template")public String fillTemplate(@RequestParam String templateFilePath, @RequestParam String outputFilePath) {log.info("準備使用模板填充數據: {}, 輸出文件路徑: {}", templateFilePath, outputFilePath); // 記錄日志List<UserData> data = new ArrayList<>();for (int i = 1; i <= 500; i++) { // 假設我們有500條數據data.add(new UserData().setName("張三" + i).setAge(20 + i % 50));}excelService.fillTemplate(templateFilePath, outputFilePath, data); // 調用模板填充方法log.info("模板填充數據完成"); // 返回成功消息return "模板填充數據成功"; // 返回成功消息}
}
7. 異常處理與日志記錄
在實際應用中,建議增加更多的異常處理邏輯和日志記錄,以便更好地調試和維護。我們已經在服務類中添加了基本的日志記錄和異常處理機制。
全局異常處理
你可以在項目中添加全局異常處理器來捕獲和處理未處理的異常:
package com.example.demo.exception;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import lombok.extern.slf4j.Slf4j;@Slf4j // 使用@Slf4j注解簡化日志記錄
@ControllerAdvice
public class GlobalExceptionHandler {/*** 處理所有異常* @param e 異常對象* @return 錯誤響應*/@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception e) {log.error("發生錯誤: ", e); // 記錄錯誤日志return new ResponseEntity<>("發生錯誤: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); // 返回錯誤消息}
}
8. 啟動應用程序
確保你的主應用程序類正確配置:
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;@SpringBootApplication
@Slf4j // 使用@Slf4j注解簡化日志記錄
public class DemoApplication {/*** 主函數,啟動Spring Boot應用* @param args 命令行參數*/public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args); // 啟動Spring Boot應用log.info("Spring Boot應用已啟動"); // 記錄啟動日志}
}
9. 測試
啟動應用程序后,你可以通過瀏覽器或Postman等工具訪問以下URL來測試Excel的讀寫功能:
- 寫入Excel:
http://localhost:8080/excel/write?filePath=/path/to/your/output.xlsx
- 讀取Excel:
http://localhost:8080/excel/read?filePath=/path/to/your/input.xlsx
總結
以上是完整的Spring Boot集成EasyExcel的詳細步驟和代碼示例,包括詳細的注釋以及實現了當單個頁面數據達到一定量時重新生成新的頁面進行寫入的功能。