Java領域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個嚴重的問題就是很是的耗內存。若是你的系統併發量不大的話可能還行,可是一旦併發上來後必定會OOM或者JVM頻繁的full gc。html
easyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱。java
64M內存1分鐘內讀取75M(46W行25列)的Excel(固然還有急速模式能更快,可是內存佔用會在100M多一點)
git
easyExcel能大大減小佔用內存的主要緣由是在解析Excel時沒有將文件數據一次性所有加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。github
下圖是easyExcel和POI在解析Excel時的對比圖。
web
easyExcel採用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理(AnalysisEventListener)。spring
上面簡要介紹了easyExcel的特色和原理,關於easyExcel的其餘問題能夠先參考下這個文章。下面就經過代碼來介紹下怎麼使用easyExcel。數據庫
下面經過一個讀取用戶信息的列子來展現下怎麼使用easyExcel。安全
step1:建立用戶信息類併發
@Data public class UserInfo extends BaseRowModel { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private int age; @ExcelProperty(index = 2) private String address; }
step2:建立AnalysisEventListener子類app
/** * 每解析一行會回調invoke()方法。 * 整個excel解析結束會執行doAfterAllAnalysed()方法 */ //有個很重要的點 不能被spring管理,要每次讀取excel都要new。 //這邊就會有一個問題:若是UserInfoDataListener中須要用到Spring中的主鍵怎麼辦? public class UserInfoDataListener extends AnalysisEventListener<UserInfo> { Logger logger = LoggerFactory.getLogger(UserInfoDataListener.class); //每次讀取100條數據就進行保存操做 private static final int BATCH_COUNT = 100; //因爲每次讀都是新new UserInfoDataListener的,因此這個list不會存在線程安全問題 List<UserInfo> list = new ArrayList<>(); //這個組件是Spring中的組件,這邊推薦兩種方法注入這個組件 //第一種就是提供一個UserInfoDataListener的構造方法,這個方法提供一個參數是UserInfoDataListener類型 //另一種方法就是將 UserInfoDataListener 這個類定義成 UserService 實現類的內部類(推薦這種方式) //private UserService userService; @Override public void invoke(UserInfo data, AnalysisContext analysisContext) { logger.info("解析到一條數據:{}", JSON.toJSONString(data)); list.add(data); if (list.size() >= BATCH_COUNT) { saveData(); // 存儲完成清理 list list.clear(); } } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { // 這裏也要保存數據,確保最後遺留的數據也存儲到數據庫 saveData(); logger.info("全部數據解析完成!"); } private void saveData() { logger.info("{}條數據,開始存儲數據庫!", list.size()); //保存數據 //userService.save(list); logger.info("存儲數據庫成功!"); } }
step3: 讀取excel
public class EasyExcelDemo { public static void main(String[] args) throws Exception { InputStream fis = new FileInputStream("D:\\UserInfo.xlsx"); AnalysisEventListener listener = new UserInfoDataListener(); ExcelReader excelReader = EasyExcel.read(fis, UserInfo.class, listener).build(); ReadSheet readSheet = EasyExcel.readSheet(0).build(); ReadSheet readSheet2 = EasyExcel.readSheet(1).build(); excelReader.read(readSheet); // 這裏千萬別忘記關閉,讀的時候會建立臨時文件,到時磁盤會崩的 excelReader.finish(); } }
只須要上面3步就能進行Excel的讀取了。
public class ExcelUtil { public static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception{ try{ fileName = URLEncoder.encode(fileName,"utf-8"); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); //此處指定了文件類型爲xls,若是是xlsx的,請自行替換修改 response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xls"); response.setHeader("Pragma", "public"); response.setHeader("Cache-Control", "no-store"); response.addHeader("Cache-Control", "max-age=0"); return response.getOutputStream(); } catch (IOException e){ throw new Exception("導出文件失敗!"); } } public static void writeExcel(HttpServletResponse response, List<? extends BaseRowModel> list, String fileName, String sheetName, Class clazz) throws Exception { ExcelWriter writer = new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLS); Sheet sheet = new Sheet(1, 0, clazz); sheet.setSheetName(sheetName); writer.write(list, sheet); writer.finish(); } }
在Controller中咱們只要像下面這種方式調用就好了。
@RequestMapping(value = "/file/testExcelDownload") public void testExcelDownload(HttpServletRequest request,HttpServletResponse response){ //如下信息從數據庫中查出 List<ExcelInfo> excelInfos = new ArrayList<>(); ExcelInfo info1 = new ExcelInfo(); ExcelInfo info2 = new ExcelInfo(); excelInfos.add(info1); excelInfos.add(info2); info1.setIssuerName("name1"); info1.setRiskLevel("level1"); info2.setIssuerName("name1"); info2.setRiskLevel("level1"); try { String fileName = "excelInfo"; String sheetName = "sheet1"; ExcelUtil.writeExcel(response, excelInfos, fileName, sheetName, ExcelInfo.class); } catch(Exception e){ log.error("模板下載失敗",e); } }
在導出Excel的部分,easyExcel還提供了自定義樣式,插入表格,插入圖片等其餘功能,還有一個比較有意思的功能就是Excel模板填充的功能。詳細的功能信息參考官方文檔。