EasyExcel是一個基於Java的簡單、省內存的讀寫Excel的開源項目。在儘量節約內存的狀況下支持讀寫百M的Excel。相對於Apache POI來講,EasyExcel是從磁盤上一行行的讀取數據,而後逐個解析,避免將大量數據加載到內存從而致使OOM。java
相關依賴:web
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency> <!-- 可選 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency>
須要導入的數據:
spring
Model類:app
@Data public class SchoolData { @ExcelProperty(value = "地址") private String address; @ExcelProperty(value = "學校名稱") private String schoolName; @ExcelProperty(value = "郵政編碼") private String postcode; @ExcelProperty(value = "收集時間") private Date collectDate; }
使用@ExcelProperty註解時,可使用兩種方式將數據列和屬性進行綁定:ide
在不使用@ExcelProperty註解時,默認Model類屬性和excel列數據按照前後順序進行綁定的。post
監聽器:測試
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import java.util.ArrayList; import java.util.List; import java.util.Map; public class SchoolDataListener extends AnalysisEventListener<SchoolData> { List<SchoolData> schoolDataList = new ArrayList<>(); //須要傳參時,經過構造方法傳進來 public SchoolDataListener() { } /** * 每解析一行數據都會進行調用 */ @Override public void invoke(SchoolData data, AnalysisContext context) { //通常數據量大的時候應該分批處理,防止數據所有加載到內存,致使OOM。 schoolDataList.add(data); } /** * 數據所有解析完成時調用 */ @Override public void doAfterAllAnalysed(AnalysisContext context) { System.out.println("數據讀取完畢:"); schoolDataList.stream().forEach(System.out::println); } /** * 每解析一行表頭都會進行調用 */ @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { System.out.println("表頭:"); headMap.forEach((k, v) -> System.out.println(v)); System.out.println(); } }
導入測試:ui
導入有如下兩種寫法,通常使用簡寫的方式就行,當須要實現複雜的功能時可能會須要另一種複雜的寫法。編碼
@Test public void importExcel() { ExcelReader excelReader = null; try { excelReader = EasyExcel.read("F:\\測試數據.xlsx", SchoolData.class, new SchoolDataListener()).build(); ReadSheet readSheet = EasyExcel.readSheet(0).build(); excelReader.read(readSheet); } finally { if (excelReader != null) { // 這裏千萬別忘記關閉,讀的時候會建立臨時文件,到時磁盤會崩的 excelReader.finish(); } } } @Test public void importExcelSimple() { //sheet() 默認讀取第一個sheet EasyExcel.read("F:\\測試數據.xlsx", SchoolData.class, new SchoolDataListener()).sheet().doRead(); }
輸出結果:.net
表頭: 學校名稱 地址 郵政編碼 收集時間 數據讀取完畢: SchoolData(address=晉寧縣古城鎮舊寨村 , schoolName=晉寧縣古城鎮古城中學, postcode=650604, collectDate=Sun Jan 05 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣二街鄉老高村 , schoolName=晉寧縣二街中學, postcode=650608, collectDate=Mon Jan 06 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣晉城鎮寺林街 , schoolName=晉寧縣晉城中學, postcode=650605, collectDate=Tue Jan 07 00:00:00 CST 2020) SchoolData(address=雲南省晉寧縣六街鄉中學 , schoolName=晉寧縣六街中學, postcode=650609, collectDate=Wed Jan 08 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣新街鄉文河村 , schoolName=晉寧縣新街中學, postcode=650606, collectDate=Thu Jan 09 00:00:00 CST 2020) SchoolData(address=晉寧縣中和鄉普照路 , schoolName=晉寧縣中和中學, postcode=650600, collectDate=Fri Jan 10 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣上蒜鄉上蒜中學 , schoolName=晉寧縣上蒜鄉上蒜中學, postcode=650607, collectDate=Sat Jan 11 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣化樂鄉化樂村 , schoolName=晉寧縣化樂鄉中學, postcode=650611, collectDate=Sun Jan 12 00:00:00 CST 2020) SchoolData(address=晉寧縣夕陽鄉夕陽街夕陽民族中學 , schoolName=晉寧縣夕陽民族中學, postcode=650603, collectDate=Mon Jan 13 00:00:00 CST 2020) SchoolData(address=雲南省昆明市晉寧縣寶峯鎮古城村委會小古城村 , schoolName=晉寧縣寶峯鎮寶峯中學, postcode=650601, collectDate=Tue Jan 14 00:00:00 CST 2020) SchoolData(address=晉寧縣雙河鄉椿樹營 , schoolName=晉寧縣雙河民族中學, postcode=650602, collectDate=Wed Jan 15 00:00:00 CST 2020) SchoolData(address=昆明市富民縣永定鎮環城南路7號永定中學 , schoolName=富民縣永定中學, postcode=650400, collectDate=Thu Jan 16 00:00:00 CST 2020) SchoolData(address=富民縣永定鎮大西山村 , schoolName=富民縣勤勞中學, postcode=650400, collectDate=Fri Jan 17 00:00:00 CST 2020) SchoolData(address=富民縣大營鎮大營街16號 , schoolName=富民縣大營中學, postcode=650400, collectDate=Sat Jan 18 00:00:00 CST 2020) SchoolData(address=雲南省昆明市羅免鄉者北村委會者北街 , schoolName=富民縣第二中學, postcode=650401, collectDate=Sun Jan 19 00:00:00 CST 2020)
模擬生成數據:
public class DataGenerate { /** * 數據生成 */ public static List<SchoolData> data() { List<SchoolData> schoolDataList = new ArrayList<>(); SchoolData schoolData; for (int i = 0; i < 15; i++) { schoolData = new SchoolData(); schoolData.setSchoolName("第" + i + "XXX中學"); schoolData.setAddress("雲南省昆明市XXXXX"); schoolData.setPostcode("123456"); schoolData.setCollectDate(new Date()); schoolDataList.add(schoolData); } return schoolDataList; } }
測試導出:
同導入,導出也有如下兩種寫法。LongestMatchColumnWidthStyleStrategy用來設置自動列寬,若是須要設置列寬、行高等屬性,能夠在Model類上添加對應註解進行設置,如@ColumnWidth、@ContentRowHeight、@HeadRowHeight等等。
@Test public void exportExcel() { ExcelWriter excelWriter = null; try { excelWriter = EasyExcel.write("F:\\測試數據_副本.xlsx", SchoolData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet("學校數據").registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build(); excelWriter.write(DataGenerate.data(), writeSheet); } finally { // 千萬別忘記finish 會幫忙關閉流 if (excelWriter != null) { excelWriter.finish(); } } } @Test public void exportExcelSimple() { EasyExcel.write("F:\\測試數據_副本.xlsx", SchoolData.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("學校數據").doWrite(DataGenerate.data()); }
導出結果:
模板:
{} 表明普通變量 {.} 表明是list的變量。
模板填充測試:
@Test public void excelTemplateFill() { ExcelWriter excelWriter = EasyExcel.write("F:\\測試數據_副本3.xlsx").withTemplate("F:\\模板.xlsx").build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); //填充列表數據 excelWriter.fill(DataGenerate.data(), fillConfig, writeSheet); //填充普通數據 Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "狗子"); map.put("date", "2021-02-19"); excelWriter.fill(map, writeSheet); excelWriter.finish(); }
填充結果:
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; @RestController public class SchoolDataController { /** * 導入Excel */ @PostMapping("/import") public String importExcel(MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream(), SchoolData.class, new SchoolDataListener()).sheet().doRead(); return "success"; } /** * 導出Excel */ @GetMapping("/export") public void exportExcel(HttpServletResponse response) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 這裏URLEncoder.encode能夠防止中文亂碼 固然和easyexcel沒有關係 String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 這裏須要設置不關閉流 EasyExcel.write(response.getOutputStream(), SchoolData.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("學校數據").doWrite(DataGenerate.data()); } }
參考:官方文檔