EasyExcel(笔记)
常用場景
1、將用戶信息導出為excel表格(導出數據…)
2、將Excel表中的信息錄入到網站數據庫(習題上傳…)
開發中經常會設計到excel的處理,如導出Excel,導入Excel到數據庫中! 操作Excel目前比較流行的就是 Apache POI 和 阿里巴巴的 easyExcel !
首先execl有兩個版本,分別是03版和07版。
通過鼠標右鍵即可觀看(以xls,xlsx結尾)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lkkw0fFK-1610874934517)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20210117141319852.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-AtA6xjSz-1610874934522)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20210117141527668.png)]
Poi(適合小數據量)
Apache POI 官網:https://poi.apache.org/
POI是Apache軟件基金會的,POI為“Poor Obfuscation Implementation”的首字母縮寫,意為“簡潔版的模糊實現”。
所以POI的主要功能是可以用Java操作Microsoft Office的相關文件,這里我們主要講Excel
小數據寫
1 .導入依賴
<dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.17</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.1</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version></dependency></dependencies>2 .開啟讀寫操作,代碼走起
無非就是對api的充分認識,接下來我們先去了解他的api
Workbook wordkbook =new HSSFWorkbook();//創建一個Workbook對象wordkbook.createSheet();//創建表名,如果不寫參數,會有默認值Row row1=sheet.createRow(0);//根據里面的數字拿到對應的行,0默認為第一行Cell cell = row1.createCell(0);//根據行對象創建單元格,這里0為第一個cell.setCellValue("");//可以給單元格賦值寫入一個Excel
package com.kuang;import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.joda.time.DateTime; import org.junit.Test;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class ExcelWriteTest {//先要有個路勁static String path="D:\JAVA---EasyExcel\TEST";@Testpublic void testWrite03(String[] args) throws IOException {//1,創建一個工作薄Workbook wordkbook =new HSSFWorkbook();//表名Sheet sheet=wordkbook.createSheet("灰灰統計表");//創建行Row row1=sheet.createRow(0);//4.創建一個單元格Cell cell = row1.createCell(0);cell.setCellValue("今日新增觀眾");Cell cell2 = row1.createCell(1);cell2.setCellValue("盧本偉");//創建行Row row2=sheet.createRow(1);//4.創建一個單元格Cell cell3 = row2.createCell(0);cell3.setCellValue("統計時間");Cell cell24= row2.createCell(1);String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss");cell24.setCellValue(time);//生成一張表 03是xls 07是xlsxFileOutputStream fileOutputStream = new FileOutputStream(path + "灰灰統計表03.xls");wordkbook.write(fileOutputStream);fileOutputStream.close();System.out.println("灰灰統計表03已生成");}@Testpublic void testWrite07() throws IOException {//1,創建一個工作薄Workbook wordkbook =new XSSFWorkbook();//表名Sheet sheet=wordkbook.createSheet("灰灰統計表");//創建行Row row1=sheet.createRow(0);//4.創建一個單元格Cell cell = row1.createCell(0);cell.setCellValue("今日新增觀眾");Cell cell2 = row1.createCell(1);cell2.setCellValue("盧本偉");//創建行Row row2=sheet.createRow(1);//4.創建一個單元格Cell cell3 = row2.createCell(0);cell3.setCellValue("統計時間");Cell cell24= row2.createCell(1);String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss");cell24.setCellValue(time);//生成一張表 03是xls 07是xlsxFileOutputStream fileOutputStream = new FileOutputStream(path + "\灰灰統計表07.xlsx");wordkbook.write(fileOutputStream);fileOutputStream.close();System.out.println("灰灰統計表07已生成");}}上面寫完后會在項目目錄下生成一個表格
03 | 07 版本的寫,就是對象不同,方法一樣的!
大數據寫
HSSF
缺點:最多只能處理65536行,否則會拋出異常
java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0…65535)
優點:過程中寫入緩存,不操作磁盤,最后一次性寫入磁盤,速度快
@Test public void testWrite03BigData() throws IOException {long begin = System.currentTimeMillis();HSSFWorkbook workbook = new HSSFWorkbook();HSSFSheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum < 65535; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("over");FileOutputStream outputStream = new FileOutputStream(path + "//testWrite03BigData");workbook.write(outputStream);outputStream.close();long end = System.currentTimeMillis();System.out.println((double) (end-begin)/1000);}XSSF
缺點:寫數據時速度非常慢,非常耗內存,也會發生內存溢出,如100萬條
優點:可以寫較大的數據量,如20萬條
@Test public void testWrite07BigData() throws IOException {long begin = System.currentTimeMillis();Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum < 655350; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("over");FileOutputStream outputStream = new FileOutputStream(path + "//testWrite03BigData.xlsx");workbook.write(outputStream);outputStream.close();long end = System.currentTimeMillis();System.out.println((double) (end-begin)/1000);}SXSSF
優點:可以寫非常大的數據量,如100萬條甚至更多條,寫數據速度快,占用更少的內存
注意:
過程中會產生臨時文件,需要清理臨時文件
默認由100條記錄被保存在內存中,如果超過這數量,則最前面的數據被寫入臨時文件 如果想自定義內存中數據的數量,可以使用new SXSSFWorkbook ( 數量 )
@Test public void testWrite07BigDataS() throws IOException {long begin = System.currentTimeMillis();Workbook workbook = new SXSSFWorkbook();Sheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("over");FileOutputStream outputStream = new FileOutputStream(path + "//testWrite07BigDataS.xlsx");workbook.write(outputStream);outputStream.close();((SXSSFWorkbook)workbook).dispose();//關閉臨時文件long end = System.currentTimeMillis();System.out.println((double) (end-begin)/1000);}SXSSFWorkbook-來至官方的解釋:實現“BigGridDemo”策略的流式XSSFWorkbook版本。這允許寫入 非常大的文件而不會耗盡內存,因為任何時候只有可配置的行部分被保存在內存中。
請注意,仍然可能會消耗大量內存,這些內存基于您正在使用的功能,例如合并區域,注釋…仍然只存 儲在內存中,因此如果廣泛使用,可能需要大量內存。
讀取單一類型的數據
這個操作跟上述的寫并沒有什么不同,不同就是方法是get而不是set
static String path="F:\\demo\\javapoi\\demopoi";@Testpublic void testRead03() throws IOException {//Sheet sheet=workbook.createSheet("統計表");//sheet操作表中元素FileInputStream fileInputStream = new FileInputStream(path + "\灰灰統計表03.xls");Workbook workbook=new HSSFWorkbook(fileInputStream);Sheet sheet = workbook.getSheetAt(0); // Sheet sheet2 = workbook.getSheet("灰灰統計表");Row row = sheet.getRow(1);Cell cell = row.getCell(0);Cell cell2 = row.getCell(1);System.out.println(cell.getStringCellValue());System.out.println(cell2.getStringCellValue());fileInputStream.close();}這里值得注意的是,使用表格對象要注意三種創建方式
- POI-HSSF
- POI-XSSF
- SXSSF
**HSSF:*Excel97-2003版本,擴展名為.xls。一個sheet最大行數*65536,最大列數256。
**XSSF:*Excel2007版本開始,擴展名為.xlsx。一個sheet最大行數*1048576,最大列數16384。
SXSSF:**是在XSSF基礎上,POI3.8版本開始提供的**支持低內存占用的操作方式,擴展名為.xlsx。
Excel版本兼容性是向下兼容。
讀取不同類型的數據
在讀取數據的時候我們需要先判斷值類型,才能用對應API
下面這個是先拿到表頭那一行,相當于數據庫的字段
FileInputStream fileInputStream = new FileInputStream(path + "數據表07.xlsx");Workbook workbook=new XSSFWorkbook(fileInputStream);Sheet sheet = workbook.getSheetAt(0);Row rowTitle = sheet.getRow(0);if(rowTitle!=null){int cellCount=rowTitle.getPhysicalNumberOfCells(); //拿到第row行的那一行的總個數for (int i = 0; i <cellCount ; i++) { //循環個數取出Cell cell = rowTitle.getCell(i);if(cell!=null){ //如果不等于空取出值int cellType = cell.getCellType(); //這里是知道我們標題是String,考慮不確定的時候怎么取String cellValue = cell.getStringCellValue();System.out.print(cellValue+"|");}}System.out.println();}下面接著讀取對應的數據,這里就需要我們剛剛講的類型判斷
int cellType=cell.getCellType();利用這個,然后判斷它的XSSFCell類型再具體輸出
//獲取表中內容int rowCount=sheet.getPhysicalNumberOfRows();for(int rowNum=1;rowNum<rowCount;rowNum++){Row rowData=sheet.getRow(rowNum); //取出對應的行if(rowData!=null){int cellCount=rowTitle.getPhysicalNumberOfCells();for(int cellNum=0;cellNum<cellCount;cellNum++){System.out.print("["+(rowNum+1+"-"+(cellNum+1)+"]"));Cell cell = rowData.getCell(cellNum);//匹配數據類型if(cell!=null){int cellType=cell.getCellType();switch (cellType){case XSSFCell.CELL_TYPE_STRING: System.out.print("字符串:"+cell.getStringCellValue());break;case XSSFCell.CELL_TYPE_BOOLEAN: System.out.print("布爾:"+cell.getBooleanCellValue());break;case XSSFCell.CELL_TYPE_NUMERIC:if(HSSFDateUtil.isCellDateFormatted(cell)){System.out.println("日期格式:"+new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd HH:mm:ss"));break;}elsecell.setCellType(XSSFCell.CELL_TYPE_STRING);System.out.print("整形:"+cell.toString());break;case XSSFCell.CELL_TYPE_BLANK: System.out.print("空");break;case XSSFCell.CELL_TYPE_ERROR: System.out.print("數據類型錯誤");break;case Cell.CELL_TYPE_FORMULA://拿到計算公式XSSFFormulaEvaluator FormulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);String formula=cell.getCellFormula();System.out.println("公式:"+formula);//CellValue evaluate = FormulaEvaluator.evaluate(cell);String cellValue=evaluate.formatAsString();System.out.println(cellValue);break;default:break;}}}}}fileInputStream.close();EasyExcel(適合大數據量)
這個的出現比poi簡單非常多,只需要認清他的對應API就可以進行操作了,即使記不清楚了,我們也可以去網站上在線COPY
https://www.yuque.com/
導入依賴
//注意它里面自帶poi依賴,如果重復帶入會報ClassNotfound <dependency> <groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.0-beta2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency>讀寫操作
我們以上面這個表格為例來進行讀寫操作,觸類旁通
寫操作
先來個實體類方便插入數據
import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data;import java.util.Date;@Data //lombok public class DemoData {@ExcelProperty("字符串標題")private String string;@ExcelProperty("日期標題")private Date date;@ExcelProperty("數字標題")private Double doubleData;/*** 忽略這個字段*/@ExcelIgnore //注意這個注解是高版本的easyexcel依賴才有private String ignore; }再來一個工具類方便我們寫數據
public class utilList {public static List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;} }進行寫
@Testpublic void simpleWrite() {// 寫法1String path="D:\JAVA---EasyExcel\TEST";String fileName = path + "\EasyTest.xlsx";// 這里 需要指定寫用哪個class去寫,然后寫到第一個sheet,名字為模板 然后文件流會自動關閉// 如果這里想使用03 則 傳入excelType參數即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());// 寫法2 // fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // // 這里 需要指定寫用哪個class去寫 // ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); // WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); // excelWriter.write(data(), writeSheet); // // 千萬別忘記finish 會幫忙關閉流 // excelWriter.finish();}寫完就有了這樣一個表格
讀操作
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.60</version> </dependency>首先我們一個監聽器,因為和poi的不同,easyExcel是spring接管的,自己監控和改寫方法
package com.example.easyExcel;import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.fastjson.JSON; import com.example.Dao.DemoDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.util.ArrayList; import java.util.List; // 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然后里面用到spring可以構造方法傳進去 public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5條存儲數據庫,實際使用中可以3000條,然后清理list ,方便內存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假設這個是一個DAO,當然有業務邏輯這個也可以是一個service。當然如果不用存儲這個對象沒用。*/private DemoDAO demoDAO;public DemoDataListener() {// 這里是demo,所以隨便new一個。實際使用如果到了spring,請使用下面的有參構造函數demoDAO = new DemoDAO();}/*** 如果使用了spring,請使用這個構造方法。每次創建Listener的時候需要把spring管理的類傳進來** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 這個每一條數據解析都會來調用** @param data* one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {LOGGER.info("解析到一條數據:{}", JSON.toJSONString(data));System.out.println( JSON.toJSONString(data));list.add(data);// 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存儲完成清理 listlist.clear();}}/*** 所有數據解析完成了 都會來調用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 這里也要保存數據,確保最后遺留的數據也存儲到數據庫saveData();LOGGER.info("所有數據解析完成!");}/*** 加上存儲數據庫*/private void saveData() {LOGGER.info("{}條數據,開始存儲數據庫!", list.size());demoDAO.save(list);LOGGER.info("存儲數據庫成功!");} }這里的saveData是為了給讀取前臺的表格之后可以執行這個然后通過下面的方法持久化到數據庫,而且這里默認是5條持久一次
/*** 假設這個是你的DAO存儲。當然還要這個類讓spring管理,當然你不用需要存儲,也不需要這個類。**/ public class DemoDAO {public void save(List<DemoData> list) {// 如果是mybatis,盡量別直接調用多次insert,自己寫一個mapper里面新增一個方法batchInsert,所有數據一次性插入} }@Override報錯
可能是jdk版本不是8
檢查java和javac
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wa1x84lP-1610874934528)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20210117165613834.png)]
開始讀的操作
/*** 最簡單的讀* <p>1. 創建excel對應的實體對象 參照{@link DemoData}* <p>2. 由于默認一行行的讀取excel,所以需要創建excel一行一行的回調監聽器,參照{@link DemoDataListener}* <p>3. 直接讀即可*/@Testpublic void simpleRead() {String fileName = path + "EasyTest.xlsx";EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();}總結
- easyExcel的確比poi方便,但是它的讀需要編寫監聽器
- 建議大數據用easyExcel,因為大數據時poi對于內存消耗非常大
- 由于apache poi和jxl,excelPOI都有一個嚴重的問題,就是非常消耗內存,特別處理數據量多時,速度慢并且時有異常發生,所以改用由阿里研發的easyExcel更可靠一些,它的官方建議對于1000行以內的采用原來poi的寫法一次讀寫,但于1000行以上的數據,有用了一行行進行解析的方案,這樣避免了內存的溢出。
- EasyExcel擴展功能很多,且Api式調用真的輕松很多
總結
以上是生活随笔為你收集整理的EasyExcel(笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 协方差计算公式 公式讲解
- 下一篇: Rest风格---ElasticSear