1.POI使用
1.1 什么是POI
POI簡介(Apache POI),Apache POI是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。
- HSSF - 提供讀寫Microsoft Excel格式檔案的功能。(.xls)
- XSSF - 提供讀寫Microsoft Excel OOXML格式檔案的功能。(.xlsx)
- HWPF - 提供讀寫Microsoft Word格式檔案的功能。
- HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
- HDGF - 提供讀寫Microsoft Visio格式檔案的功能。
1.2 官網
官網可以找到文檔和每個版本的下載地址
2.xls寫-03和07
2.1、創建項目
1、創建一個普通的maven項目
項目名:excel_poi
2、pom中引入xml相關依賴
<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>
<!--test-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.2、xls寫-03和07的區別
區別1.格式不一樣,03是xls,07是xlsx。
區別2:別的沒啥區別。
1、03
public class ExcelWriteTest {
@Test
public void testWrite03() throws IOException {
// 創建新的Excel 工作簿
Workbook workbook = new HSSFWorkbook();
// 在Excel工作簿中建一工作表,其名為缺省值 Sheet0
//Sheet sheet = workbook.createSheet();
// 如要新建一名為"會員登錄統計"的工作表,其語句為:
Sheet sheet = workbook.createSheet("會員登錄統計");
// 創建行(row 1)
Row row1 = sheet.createRow(0);
// 創建單元格(col 1-1)
Cell cell11 = row1.createCell(0);
cell11.setCellValue("今日人數");
// 創建單元格(col 1-2)
Cell cell12 = row1.createCell(1);
cell12.setCellValue(666);
// 創建行(row 2)
Row row2 = sheet.createRow(1);
// 創建單元格(col 2-1)
Cell cell21 = row2.createCell(0);
cell21.setCellValue("統計時間");
//創建單元格(第三列)
Cell cell22 = row2.createCell(1);
String dateTime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
cell22.setCellValue(dateTime);
// 新建一輸出文件流(注意:要先創建文件夾)
FileOutputStream out = new FileOutputStream("d:/excel-poi/test-write03.xls");
// 把相應的Excel 工作簿存盤
workbook.write(out);
// 操作結束,關閉文件
out.close();
System.out.println("文件生成成功");
}
}
2 、07
@Test
public void testWrite07() throws IOException {
// 創建新的Excel 工作簿
Workbook workbook = new XSSFWorkbook();
......
// 新建一輸出文件流(注意:要先創建文件夾)
FileOutputStream out = new FileOutputStream("d:/excel-poi/test-write07.xlsx");
......
}
2.3、大文件寫-HSSF、XSSF和SXSSF
1、使用HSSF
缺點:最多只能處理65536行,否則會拋出異常
java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
優點:過程中寫入緩存,不操作磁盤,最后一次性寫入磁盤,速度快
@Test
public void testWrite03BigData() throws IOException {
//記錄開始時間
long begin = System.currentTimeMillis();
//創建一個SXSSFWorkbook
Workbook workbook = new HSSFWorkbook();
//創建一個sheet
Sheet sheet = workbook.createSheet();
//xls文件最大支持65536行
for (int rowNum = 0; rowNum < 65536; rowNum++) {
//創建一個行
Row row = sheet.createRow(rowNum);
for (int cellNum = 0; cellNum < 10; cellNum++) {//創建單元格
Cell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
System.out.println("done");
FileOutputStream out = new FileOutputStream("d:/excel-poi/test-write03-bigdata.xls");
workbook.write(out);
// 操作結束,關閉文件
out.close();
//記錄結束時間
long end = System.currentTimeMillis();
System.out.println((double)(end - begin)/1000);
}
2、使用XSSF
缺點:寫數據時速度非常慢,非常耗內存,也會發生內存溢出,如100萬條
優點:可以寫較大的數據量,如20萬條
@Test
public void testWrite07BigData() throws IOException {
//記錄開始時間
long begin = System.currentTimeMillis();
//創建一個XSSFWorkbook
Workbook workbook = new XSSFWorkbook();
......
FileOutputStream out = new FileOutputStream("d:/excel-poi/test-write07-bigdata.xlsx");
......
}
3、使用SXSSF
優點:可以寫非常大的數據量,如100萬條甚至更多條,寫數據速度快,占用更少的內存
注意:
過程中會產生臨時文件,需要清理臨時文件
默認由100條記錄被保存在內存中,如果查過這數量,則最前面的數據被寫入臨時文件
如果想自定義內存中數據的數量,可以使用new SXSSFWorkbook(數量)
@Test
public void testWrite07BigDataFast() throws IOException {
//記錄開始時間
long begin = System.currentTimeMillis();
//創建一個SXSSFWorkbook
Workbook workbook = new SXSSFWorkbook();
......
FileOutputStream out = new FileOutputStream("d:/excel-poi/test-write07-bigdata-fast.xlsx");
workbook.write(out);
// 操作結束,關閉文件
out.close();
//清除臨時文件
((SXSSFWorkbook)workbook).dispose();
//記錄結束時間
long end = System.currentTimeMillis();
System.out.println((double)(end - begin)/1000);
}
SXSSFWorkbook-來至官方的解釋:實現“BigGridDemo”策略的流式XSSFWorkbook版本。這允許寫入非常大的文件而不會耗盡內存,因為任何時候只有可配置的行部分被保存在內存中。
請注意,仍然可能會消耗大量內存,這些內存基於您正在使用的功能,例如合並區域,注釋......仍然只存儲在內存中,因此如果廣泛使用,可能需要大量內存。
3、xls讀-03和07
1、03
package com.atguigu.excelpoi;
public class ExcelReadTest {
@Test
public void testRead03() throws Exception{
InputStream is = new FileInputStream("d:/excel-poi/商品表-03.xls");
Workbook workbook = new HSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 讀取第一行第一列
Row row = sheet.getRow(0);
Cell cell = row.getCell(0);
// 輸出單元內容
System.out.println(cell.getStringCellValue());
// 操作結束,關閉文件
is.close();
}
}
2、07
@Test
public void testRead07() throws Exception{
InputStream is = new FileInputStream("d:/excel-poi/商品表-07.xlsx");
Workbook workbook = new XSSFWorkbook(is);
......
}
3、讀取不同類型的數據
1、讀取不同的數據類型
@Test
public void testCellType() throws Exception {
InputStream is = new FileInputStream("d:/excel-poi/會員消費商品明細表.xls");
Workbook workbook = new HSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 讀取標題所有內容
Row rowTitle = sheet.getRow(0);
if (rowTitle != null) {// 行不為空
// 讀取cell
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.print(cellValue + "|");
}
}
System.out.println();
}
// 讀取商品列表數據
int rowCount = sheet.getPhysicalNumberOfRows();
for (int rowNum = 1; rowNum < rowCount; rowNum++) {
Row rowData = sheet.getRow(rowNum);
if (rowData != null) {// 行不為空
// 讀取cell
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://字符串
System.out.print("【STRING】");
cellValue = cell.getStringCellValue();
break;
case HSSFCell.CELL_TYPE_BOOLEAN://布爾
System.out.print("【BOOLEAN】");
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case HSSFCell.CELL_TYPE_BLANK://空
System.out.print("【BLANK】");
break;
case HSSFCell.CELL_TYPE_NUMERIC:
System.out.print("【NUMERIC】");
//cellValue = String.valueOf(cell.getNumericCellValue());
if (HSSFDateUtil.isCellDateFormatted(cell)) {//日期
System.out.print("【日期】");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
} else {
// 不是日期格式,則防止當數字過長時以科學計數法顯示
System.out.print("【轉換成字符串】");
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
cellValue = cell.toString();
}
break;
case Cell.CELL_TYPE_ERROR:
System.out.print("【數據類型錯誤】");
break;
}
System.out.println(cellValue);
}
}
}
}
is.close();
}
2、計算公式
@Test
public void testFormula() throws Exception{
InputStream is = new FileInputStream("d:/excel-poi/計算公式.xls");
Workbook workbook = new HSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 讀取第五行第一列
Row row = sheet.getRow(4);
Cell cell = row.getCell(0);
//公式計算器
FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
// 輸出單元內容
int cellType = cell.getCellType();
switch (cellType) {
case Cell.CELL_TYPE_FORMULA://2
//得到公式
String formula = cell.getCellFormula();
System.out.print(formula);
CellValue evaluate = formulaEvaluator.evaluate(cell);
//String cellValue = String.valueOf(evaluate.getNumberValue());
String cellValue = evaluate.formatAsString();
System.out.println(cellValue);
break;
}
}
4.EasyExcel使用
4.1、Excel導入導出的應用場景
1、數據導入:減輕錄入工作量
2、數據導出:統計信息歸檔
3、數據傳輸:異構系統之間數據傳輸
4.2、EasyExcel簡介
- Java領域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個嚴重的問題就是非常的耗內存。如果你的系統並發量不大的話可能還行,但是一旦並發上來后一定會OOM或者JVM頻繁的full gc。
- EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱。EasyExcel能大大減少占用內存的主要原因是在解析Excel時沒有將文件數據一次性全部加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。
- EasyExcel采用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理(AnalysisEventListener)。
5.EasyExcel寫入
5.1 創建項目,實現EasyExcel對Excel寫操作
1、創建一個普通的maven項目
項目名:excel-easydemo
2、pom中引入xml相關依賴
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
3、創建實體類
設置表頭和添加的數據字段
import com.alibaba.excel.annotation.ExcelProperty;
//設置表頭和添加的數據字段
public class DemoData {
//設置表頭名稱
@ExcelProperty("學生編號")
private int sno;
//設置表頭名稱
@ExcelProperty("學生姓名")
private String sname;
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "DemoData{" +
"sno=" + sno +
", sname='" + sname + '\'' +
'}';
}
}
4、實現寫操作
(1)創建方法循環設置要添加到Excel的數據
//循環設置要添加的數據,最終封裝到list集合中
private static List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setSno(i);
data.setSname("張三"+i);
list.add(data);
}
return list;
}
(2)實現最終的添加操作(寫法一)
public static void main(String[] args) throws Exception {
// 寫法1
String fileName = "F:\\11.xlsx";
// 這里 需要指定寫用哪個class去寫,然后寫到第一個sheet,名字為模板 然后文件流會自動關閉
// 如果這里想使用03 則 傳入excelType參數即可
EasyExcel.write(fileName, DemoData.class).sheet("寫入方法一").doWrite(data());
}
(3)實現最終的添加操作(寫法二)
public static void main(String[] args) throws Exception {
// 寫法2,方法二需要手動關閉流
String fileName = "F:\\112.xlsx";
// 這里 需要指定寫用哪個class去寫
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("寫入方法二").build();
excelWriter.write(data(), writeSheet);
/// 千萬別忘記finish 會幫忙關閉流
excelWriter.finish();
}
6.EasyExcel讀取
6.1、實現EasyExcel對Excel讀操作
1、創建實體類
import com.alibaba.excel.annotation.ExcelProperty;
public class ReadData {
//設置列對應的屬性
@ExcelProperty(index = 0)
private int sid;
//設置列對應的屬性
@ExcelProperty(index = 1)
private String sname;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "ReadData{" +
"sid=" + sid +
", sname='" + sname + '\'' +
'}';
}
}
2、創建讀取操作的監聽器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
//創建讀取excel監聽器
public class ExcelListener extends AnalysisEventListener<ReadData> {
//創建list集合封裝最終的數據
List<ReadData> list = new ArrayList<ReadData>();
//一行一行去讀取excle內容
@Override
public void invoke(ReadData user, AnalysisContext analysisContext) {
System.out.println("***"+user);
list.add(user);
}
//讀取excel表頭信息
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表頭信息:"+headMap);
}
//讀取完成后執行
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
3、調用實現最終的讀取
public static void main(String[] args) throws Exception {
// 寫法1:
String fileName = "F:\\01.xlsx";
// 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉
EasyExcel.read(fileName, ReadData.class, new ExcelListener()).sheet().doRead();
// 寫法2:
InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));
ExcelReader excelReader = EasyExcel.read(in, ReadData.class, new ExcelListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 這里千萬別忘記關閉,讀的時候會創建臨時文件,到時磁盤會崩的
excelReader.finish();
}