★應用場景
1.將用戶信息導出為excel表格(導出數據...)
2.將excel表中的信息錄入到網站數據庫
操作excel目前比較流行的就是Apache POI 和阿里巴巴的easyExcel
★Apache POI
官網:https://poi.apache.org/ 使用起來相對比較麻煩,消耗內存
基本功能
HSSF:提供讀寫excel格式檔案的功能(03版本,行數最多65536行)
XSSF:提供讀寫excel、ooxml格式檔案的功能(07版本。l理論 行數沒有限制)
HWPF:提供讀寫我認得格式檔案的功能
HSLF:提供讀寫PowerPoint格式檔案的功能
HDGF:提供讀寫Visio格式檔案的功能
★easyExcel
官網:https://github.com/alibaba/easyexcel
在poi基礎上優化,降低內存使用,開發使用更簡單。
★POI和easyExcel區別
POI:一次性將數據加載到內存,可能出現內存溢出。oom問題。
easyExcel:一次一行操作。
★POI-Excel寫
1.創建一個空的Maven項目

2.引入xls(03版本)和xlsx(07版本)依賴
<dependencies>
<!--xls(03)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<!--xlsx(07)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</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.12</version>
</dependency>
</dependencies>

步驟1:工作簿 --> 步驟2:工作表 --> 步驟3:行 --> 步驟4:列
package com.ckfuture; 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.security.spec.RSAOtherPrimeInfo; public class ExcelWriteText { String PATH="H:\\javaProject\\javaExcel"; @Test public void testWrite03() throws Exception { //1.創建一個工作薄 Workbook workbook=new HSSFWorkbook(); //2.創建一個工作表 Sheet sheet=workbook.createSheet("創客未來統計表"); //3.創建一個行 Row row1=sheet.createRow(0);//第一行 //4.創建一個單元格 Cell cell11 = row1.createCell(0);//第一列 cell11.setCellValue("今日的新增觀眾"); Cell cell12 = row1.createCell(1);//第二列 cell12.setCellValue(777); //第二行 Row row2=sheet.createRow(1);//第二行 Cell cell21 = row2.createCell(0);//第一列 cell21.setCellValue("統計時間"); Cell cell22 = row2.createCell(1);//第二列 String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss"); cell22.setCellValue(time); //生成一張表(IO流) FileOutputStream fileOutputStream = new FileOutputStream(PATH + "/創客未來03.xls"); //輸出 workbook.write(fileOutputStream); //關閉流 fileOutputStream.close(); System.out.println("創客未來03 生成完畢!"); } }

@Test public void testWrite07() throws Exception { //1.創建一個工作薄 Workbook workbook=new XSSFWorkbook(); //2.創建一個工作表 Sheet sheet=workbook.createSheet("創客未來統計表"); //3.創建一個行 Row row1=sheet.createRow(0);//第一行 //4.創建一個單元格 Cell cell11 = row1.createCell(0);//第一列 cell11.setCellValue("今日的新增觀眾"); Cell cell12 = row1.createCell(1);//第二列 cell12.setCellValue(777); //第二行 Row row2=sheet.createRow(1);//第二行 Cell cell21 = row2.createCell(0);//第一列 cell21.setCellValue("統計時間"); Cell cell22 = row2.createCell(1);//第二列 String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss"); cell22.setCellValue(time); //生成一張表(IO流) FileOutputStream fileOutputStream = new FileOutputStream(PATH + "/創客未來07.xlsx"); //輸出 workbook.write(fileOutputStream); //關閉流 fileOutputStream.close(); System.out.println("創客未來07 生成完畢!"); }

注意對象的一個區別,文件后綴!
數據批量導入
03版本批量導入
@Test public void testWrite03BigData() throws Exception { //時間 long begin = System.currentTimeMillis(); //創建一個工作簿 Workbook workbook = new HSSFWorkbook(); //創建一個表 Sheet sheet = workbook.createSheet(); //寫入數據 for (int rowNum = 0; rowNum < 65536; rowNum++) { Row row = sheet.createRow(rowNum);//每一行 for (int cellNum = 0; cellNum < 10; cellNum++) { row.createCell(cellNum).setCellValue(cellNum);//每一列 } } System.out.println("write over"); FileOutputStream outputStream = new FileOutputStream(PATH + "/testWrite03BigData.xls"); workbook.write(outputStream); outputStream.close(); long end = System.currentTimeMillis(); System.out.println((double)(end-begin)/1000); }

07版本批量導入
@Test public void testWrite07BigData() throws Exception { //時間 long begin = System.currentTimeMillis(); //創建一個工作簿 Workbook workbook = new XSSFWorkbook(); //創建一個表 Sheet sheet = workbook.createSheet(); //寫入數據 for (int rowNum = 0; rowNum < 65537; rowNum++) { Row row = sheet.createRow(rowNum);//每一行 for (int cellNum = 0; cellNum < 10; cellNum++) { row.createCell(cellNum).setCellValue(cellNum);//每一列 } } System.out.println("write over"); FileOutputStream outputStream = new FileOutputStream(PATH + "/testWrite07BigData.xlsx"); workbook.write(outputStream); outputStream.close(); long end = System.currentTimeMillis(); System.out.println((double)(end-begin)/1000); }

07版本耗時比較長。如果優化可以才用 SXXFF
@Test public void testWrite07BigDataS() throws Exception { //時間 long begin = System.currentTimeMillis(); //創建一個工作簿 Workbook workbook = new SXSSFWorkbook(); //創建一個表 Sheet sheet = workbook.createSheet(); //寫入數據 for (int rowNum = 0; rowNum < 65537; rowNum++) { Row row = sheet.createRow(rowNum);//每一行 for (int cellNum = 0; cellNum < 10; cellNum++) { row.createCell(cellNum).setCellValue(cellNum);//每一列 } } System.out.println("write 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 允許寫入非常大的文件而不會耗盡內存,但仍然會消耗大量內存,如果廣泛使用需要大量內存。
★POI-Excel讀
1.簡單讀取,讀取的excel文件數據類型是相同
03版本讀取
package com.ckfuture;
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.FileInputStream;
import java.io.FileOutputStream;
public class ExcelReadText {
String PATH="H:\\javaProject\\javaExcel";
@Test
public void testRead03() throws Exception {
//獲取文件流
FileInputStream inputStream = new FileInputStream(PATH + "/創客未來03.xls");
//1.創建一個工作薄,使用excel能操作的這邊它都可以操作
Workbook workbook=new HSSFWorkbook(inputStream);
//2.得到表
Sheet sheetAt = workbook.getSheetAt(0);
//3.得到行
Row row = sheetAt.getRow(0);//獲取第一行
//4.得到列
Cell cell = row.getCell(0);//獲取第一列
//讀取值的時候,一定需要注意類型
//getStringCellValue 字符串類型
System.out.println(cell.getStringCellValue());
//關閉流
inputStream.close();
}
}

07版本讀取
@Test public void testRead07() throws Exception { //獲取文件流 FileInputStream inputStream = new FileInputStream(PATH + "/創客未來07.xlsx"); //1.創建一個工作薄,使用excel能操作的這邊它都可以操作 Workbook workbook=new XSSFWorkbook(inputStream); //2.得到表 Sheet sheetAt = workbook.getSheetAt(0); //3.得到行 Row row = sheetAt.getRow(0);//獲取第一行 //4.得到列 Cell cell = row.getCell(0);//獲取第一列 //讀取值的時候,一定需要注意類型 //getStringCellValue 字符串類型 System.out.println(cell.getStringCellValue()); //關閉流 inputStream.close(); }

2.讀取不同類型的數據
@Test public void testCellType() throws Exception { //獲取文件流 FileInputStream inputStream = new FileInputStream(PATH + "/創客未來03.xls"); //1.創建一個工作薄,使用excel能操作的這邊它都可以操作 Workbook workbook=new HSSFWorkbook(inputStream); Sheet sheet = workbook.getSheetAt(0); //獲取標題內容 Row rowTitle = sheet.getRow(0); if(rowTitle!=null){ //獲取一行中有多少列是存在數據 int cellCount = rowTitle.getPhysicalNumberOfCells(); for (int cellNum = 0; cellNum < cellCount; cellNum++) { Cell cell = rowTitle.getCell(cellNum); if(cell!=null){ int cellType = cell.getCellType(); String cellValue = cell.getStringCellValue(); System.out.println(cellValue); } } System.out.println(); } //獲取表中的內容 int rowCount = sheet.getPhysicalNumberOfRows();//獲取excel表格中的行數 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(); String cellValue=""; switch (cellType){ case HSSFCell.CELL_TYPE_STRING://字符串 cellValue=cell.getStringCellValue(); break; case HSSFCell.CELL_TYPE_BOOLEAN://布爾 cellValue=String.valueOf(cell.getBooleanCellValue()); break; case HSSFCell.CELL_TYPE_BLANK://空 //空值不需要返回 break; case HSSFCell.CELL_TYPE_NUMERIC://數字(日期、普通數字) if(HSSFDateUtil.isCellDateFormatted(cell)){//日期 Date date = cell.getDateCellValue(); cellValue= new DateTime(date).toString("yyyy-MM-dd"); }else {//普通數字 //不是日期格式,防止數字過長! cell.setCellType(HSSFCell.CELL_TYPE_STRING); cellValue=cell.toString(); } break; case HSSFCell.CELL_TYPE_ERROR://錯誤 //數據類型錯誤不需要返回 break; } System.out.println(cellValue); } } } } //關閉流 inputStream.close(); }

3.讀取計算公式
@Test public void testFormula() throws Exception { FileInputStream inputStream = new FileInputStream(PATH + "/公式.xls"); Workbook workbook = new HSSFWorkbook(inputStream); Sheet sheet = workbook.getSheetAt(0); Row row = sheet.getRow(3); Cell cell = row.getCell(0); //拿到計算公式 eval FormulaEvaluator FormulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook); //輸出單元格的內容 int cellType = cell.getCellType(); switch(cellType){ case Cell.CELL_TYPE_FORMULA://公式 String formula = cell.getCellFormula(); System.out.println(formula); //計算 CellValue evaluate = FormulaEvaluator.evaluate(cell); String cellValue = evaluate.formatAsString(); System.out.println(cellValue); break; } }

★EasyExcel操作(alibaba開源項目)
1.導入依賴
<!--導入easyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.0-beta2</version>
</dependency>
2.建立一個實體類(easyexcel的寫入規則)
package com.ckfuture.easy; import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.util.Date; @Data public class DemoData { @ExcelProperty("字符串標題") private String string; @ExcelProperty("日期標題") private Date date; @ExcelProperty("數字標題") private Double doubleData; @ExcelIgnore private String ignore; public void setString(String s) { this.string=s; } public void setDate(Date date) { this.date=date; } public void setDoubleData(double v) { this.doubleData=v; } }
3.建立測試類 EasyTest 寫操作
package com.ckfuture.easy;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class EasyTest {
String PATH="H:\\javaProject\\javaExcel";
private 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;
}
//根據list 寫入excel
@Test
public void simpleWrite(){
//寫法1
String fileName=PATH+"/EasyTest.xlsx";
//這里需要指定寫用哪個class去寫,然后寫到第一給sheet,名字為模板 然后文件流會自動關閉
//write(fileName,格式類)
//sheet(表名)
//doWrite(數據)
EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data());
//寫法2
// fileName=PATH+"/EasyTest2.xlsx";
// ExcelWriter excelWriter=EasyExcel.write(fileName,DemoData.class).build();
// WriteSheet writeSheet=EasyExcel.writerSheet("模板").build();
// excelWriter.write(data(),writeSheet);
// //finish幫忙關閉流
// excelWriter.finish();
}
}

4.讀操作 首先創建 監聽器
package com.ckfuture.easy; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.fastjson.JSON; 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); private static final int BATCH_COUNT = 5; List<DemoData> list = new ArrayList<DemoData>(); private DemoDAO demoDAO; public DemoDataListener() { // 這里是demo,所以隨便new一個。實際使用如果到了spring,請使用下面的有參構造函數 demoDAO = new DemoDAO(); } public DemoDataListener(DemoDAO demoDAO) { this.demoDAO = demoDAO; } // 讀取數據會執行 invoke 方法 // DemoData 類型 // AnalysisContext 分析上問 @Override public void invoke(DemoData data, AnalysisContext context) { System.out.println(JSON.toJSONString(data)); list.add(data); // 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOM if (list.size() >= BATCH_COUNT) { saveData(); // 持久化邏輯! // 存儲完成清理 list list.clear(); } } /** * 所有數據解析完成了 都會來調用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 這里也要保存數據,確保最后遺留的數據也存儲到數據庫 saveData(); LOGGER.info("所有數據解析完成!"); } /** * 加上存儲數據庫 */ private void saveData() { LOGGER.info("{}條數據,開始存儲數據庫!", list.size()); demoDAO.save(list); LOGGER.info("存儲數據庫成功!"); } }
5.讀操作 再創建持久化層用於插入數據庫
package com.ckfuture.easy; import java.util.List; public class DemoDAO { public void save(List<DemoData> list) { // 持久化操作! // 如果是mybatis,盡量別直接調用多次insert,自己寫一個mapper里面新增一個方法batchInsert,所有數據一次性插入 } }
6.讀操作 測試
@Test public void simpleRead() { // 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然后里面用到spring可以構造方法傳進去 // 寫法1: String fileName = PATH + "/EasyTest.xlsx"; // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉 // 重點注意讀取的邏輯 DemoDataListener EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); }

