點擊上方藍色字體,選擇「標星公眾號」
優質文章,第一時間送達
POI
Apache POI是Apache軟體基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。
結構
HSSF - 提供讀寫Microsoft Excel格式檔案的功能。
XSSF - 提供讀寫Microsoft Excel OOXML格式檔案的功能。
HWPF - 提供讀寫Microsoft Word格式檔案的功能。
HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
HDGF - 提供讀寫Microsoft Visio格式檔案的功能。
應用
1.將用戶信息等文件導出為excel表格(導出數據)
2.將用戶信息等文件從excel中導入到資料庫等(導入數據)
Excel
Excel中的對象
(1)工作簿:WorkBook
(2)工作表:Sheet
(3)行:Row
(4)列:Cell
excel2003版和excel2007版的區別
1、保存文檔的格式不同
(1)Excel2003的保存格式為xxx.xls,其後綴名名為.xls。
(2)Excel2007的保存格式為xxx.xlsx,其後綴名名為.xlsx。
2、打開的文件類型不同
(1)Excel2003隻能夠打開後綴名為.xls的Excel文檔,打開後綴名為.xlsx的Excel文檔時,出現的是亂碼。
(2)Excel2007不僅能夠打開後綴名為.xls的Excel文檔,也能打開後綴名為.xlsx的Excel文檔。
3、可用的行和列不同
(1)Excel2003表格共有65536行,256列。
(2)Excel2007表格共有1048576行,16384列。
關於WorkBook
POI中的WorkBook接口下的三個實現類
HSSFWorkbook 是對03版本的excel文件操作的類
XSSFWorkbook 是對07版本的excel文件操作的類
SXSSFWorkbook Super XSSFWorkbook,是升級版的XSSFWorkbook,更快速的去對07版本的excel文件進行處理
下面我們將會對XSSFworkbook和SXSSFworkbook類對excel文件的操作進行時間上的對比。
POI的寫入操作
1.HSSFWorkbook的寫入操作
在maven中導入依賴
<!-- excel.xls(03版本的excel依賴) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<!-- excel.xlsx(07版本的excel依賴) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<!-- 日期格式化工具 -->
<dependency>
<groupId>org.wso2.orbit.joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4.wso2v1</version>
</dependency>
<!-- 測試工具 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>設置一個全局變量PATH
String PATH = "C:\\Users\\26090\\IdeaProjects\\cn.pdsu.wbb\\src" ;運行下面代碼,生成一個名為 03版excel.xls 的excel文件
@Test
public void testWrite03() throws Exception {
// 1.創建一個工作簿
Workbook workbook = new HSSFWorkbook() ;
// 2.創建一個工作表
Sheet sheet = workbook.createSheet("POI初學者") ;
// 3.創建一個行
Row row1 = sheet.createRow(0) ;
// 4.創建一個單元格(這裡以行坐標和列坐標創建了一個單元格(0,0))
Cell cell1 = row1.createCell(0) ;
// 向單元格內寫入數據
cell1.setCellValue("姓名");
// 創建單元格(0,1)
Cell cell2 = row1.createCell(1) ;
cell2.setCellValue("張三");
// 創建行
Row row2 = sheet.createRow(1) ;
Cell cell3 = row2.createCell(0);
cell3.setCellValue("時間");
Cell cell4 = row2.createCell(1);
// 創建日期並格式化
String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
cell4.setCellValue(time);
// 將其生成一張表(使用IO流) 03版的excel文件的擴展名是xls
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版excel.xls") ;
// 輸出
workbook.write(fileOutputStream);
// 關閉流
fileOutputStream.close();
System.out.println("03版excel.xls文件生成完畢.");
}代碼執行後在src目錄下生成的excel文件
打開文件可以看到其中的數據
2.XSSFWorkbook的寫入操作
@Test
public void testWrite07() throws Exception {
// 1.創建一個工作簿
Workbook workbook = new XSSFWorkbook() ;
// 2.創建一個工作表
Sheet sheet = workbook.createSheet("POI初學者") ;
// 3.創建一個行
Row row1 = sheet.createRow(0) ;
// 4.創建一個單元格(這裡以行坐標和列坐標創建了一個單元格(0,0))
Cell cell1 = row1.createCell(0) ;
// 向單元格內寫入數據
cell1.setCellValue("姓名");
// 創建單元格(0,1)
Cell cell2 = row1.createCell(1) ;
cell2.setCellValue("張三");
// 創建行
Row row2 = sheet.createRow(1) ;
Cell cell3 = row2.createCell(0);
cell3.setCellValue("時間");
Cell cell4 = row2.createCell(1);
// 創建日期並格式化
String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
cell4.setCellValue(time);
// 將其生成一張表(使用IO流)
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版excel.xlsx") ;
// 輸出
workbook.write(fileOutputStream);
// 關閉流
fileOutputStream.close();
System.out.println("07版excel.xlsx文件生成完畢.");
}代碼執行後在src目錄下生成的.xlsx文件
打開文件
操作較大數據量
在我們的日常應用中一般都是將數據批量的導入或導出的批量,下面我們就來看一下如何進行大數據量的導入
1.HSSF
缺點:關於03版的excel,因為其最多只有65536行,當我們導入的數據超過65536行時,它就會拋出異常
優點:再數據操作過程中,其將數據寫入緩存,不操作磁碟,最後再一次性寫入磁碟,操作速度快
數據批量導入,導入65536行數據到03版的excel文件中
@Test
public void testWriteBigData03() throws Exception {
// 開始時間
long begin = System.currentTimeMillis() ;
// 1.創建一個工作簿
Workbook workbook = new HSSFWorkbook() ;
// 2.創建一個工作表
Sheet sheet = workbook.createSheet("POI初學者") ;
// 寫入數據
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); // 設置值
}
}
// 將其生成一張表(使用IO流)
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版bigData_excel.xls") ;
// 輸出
workbook.write(fileOutputStream);
// 關閉流
fileOutputStream.close();
long end = System.currentTimeMillis() ;
System.out.println((double) (end-begin)/1000);
System.out.println("03版excel.xls文件生成完畢.");
}可以看到耗時5.147s
而從生成的文件可以看到數據只到65536行,其下方沒有空行
將生成的行數改為65537行時再次運行程序
此時的程序拋出了異常,我們在上面說過,03版的excel最多只能存儲65536行數據,超過65536行就會異常。
2.XSSF
缺點:寫入數據時數度較慢,非常的消耗內存,也有可能回發生內存溢出的情況
優點:可以寫入較多的數據量
數據批量導入,導入65536行數據到07版的excel文件中
@Test
public void testWriteBigData07() throws Exception {
// 開始時間
long begin = System.currentTimeMillis() ;
// 1.創建一個工作簿
Workbook workbook = new XSSFWorkbook() ;
// 2.創建一個工作表
Sheet sheet = workbook.createSheet("POI初學者") ;
// 寫入數據
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); // 設置值
}
}
// 將其生成一張表(使用IO流)
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版bigData_excel.xlsx") ;
// 輸出
workbook.write(fileOutputStream);
// 關閉流
fileOutputStream.close();
long end = System.currentTimeMillis() ;
System.out.println((double) (end-begin)/1000);
System.out.println("07版excel.xlsx文件生成完畢.");
}耗時10.972s,比起03版的耗時更長
可以看到在07版的excel文件中數據生成到6557行後其下方還是有空行
3.SXSSF
優點:可以寫很大的數據量,切寫數據的速度快,佔用內存更少
在使用SXSSFWorkbook操作文件的過程中回生成臨時文件,需要對臨時文件進行清理
((SXSSFWorkbook)workbook).dispose() ; // 刪除臨時文件數據批量導入,使用SXSSFWorkbook類對象導入65536行數據到07版的excel文件中
@Test
public void testWriteBigDataS07() throws Exception {
// 開始時間
long begin = System.currentTimeMillis() ;
// 1.創建一個工作簿
Workbook workbook = new SXSSFWorkbook() ;
// 2.創建一個工作表
Sheet sheet = workbook.createSheet("POI初學者") ;
// 寫入數據
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); // 設置值
}
}
// 將其生成一張表(使用IO流)
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版bigDataS_excel.xlsx") ;
// 輸出
workbook.write(fileOutputStream);
// 關閉流
fileOutputStream.close();
((SXSSFWorkbook)workbook).dispose() ; // 刪除臨時文件
long end = System.currentTimeMillis() ;
System.out.println((double) (end-begin)/1000);
System.out.println("SXSSF_07版excel.xlsx文件生成完畢.");
}處理相同的數據量耗時比之XSSFWorkbook要少
生成的excel文件
POI的讀取操作
因為excel版本的不同所以對它的操作也分為兩種
1.對03版本excel的操作
對於一個單元格數據的讀取
@Test
public void testRead01() throws Exception {
// 獲取文件流
FileInputStream inputStream = new FileInputStream(PATH + "src03版excel.xls") ;
// 創建一個工作簿,用於操作excel的各種功能
Workbook workbook = new HSSFWorkbook(inputStream) ;
// 得到表
Sheet sheet = workbook.getSheetAt(0) ;
// 得到行
Row row = sheet.getRow(0) ;
// 得到列
Cell cell = row.getCell(0) ;
System.out.println(cell.getStringCellValue());
inputStream.close();
}在對excel文件中數據的獲取時,我們需要針對不同的數據類型做出不同的獲取方法
2.對07版本excel的操作
@Test
public void testRead02() throws Exception {
// 獲取文件流
FileInputStream inputStream = new FileInputStream(PATH + "src07版excel.xlsx") ;
// 創建一個工作簿,用於操作excel的各種功能
Workbook workbook = new XSSFWorkbook(inputStream) ;
// 得到表
Sheet sheet = workbook.getSheetAt(0) ;
// 得到行
Row row = sheet.getRow(0) ;
// 得到列
Cell cell = row.getCell(0) ;
System.out.println(cell.getStringCellValue());
inputStream.close();
}3.讀取excel中不同的數據類型
先將表頭數據讀取出來,再將表中的數據進行類型的判斷並輸出
@Test
public void testRead03() throws Exception {
// 獲取文件流
FileInputStream inputStream = new FileInputStream(PATH + "src\\excel讀操作.xlsx") ;
// 創建一個工作簿,用於操作excel的各種功能
Workbook workbook = new XSSFWorkbook(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) ;
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) {
// 讀取列
int cellCount = rowTitle.getPhysicalNumberOfCells() ;
for(int cellNum = 0 ;cellNum < cellCount ; cellNum++) {
Cell cell = rowData.getCell(cellNum) ;
// 匹配列的數據類型
if(cell!=null) {
CellType cellType = cell.getCellTypeEnum() ; // 獲取數據的類型
String cellValue = "";
switch (cellType) {
case STRING : // 字符串
System.out.print("【String】");
cellValue = cell.getStringCellValue() ;
break;
case BOOLEAN : // 布爾類型
System.out.print("【Boolean】");
cellValue = String.valueOf(cell.getBooleanCellValue()) ;
break;
case BLANK : // 空
System.out.print("【Blank】");
break;
case NUMERIC : // 數字(數字中分為日期和普通數字)
System.out.print("【Number】");
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(CellType.STRING);
cellValue = cell.toString() ;
}
break;
case ERROR : // 錯誤
System.out.print("【Error】");
break;
}
System.out.println(cellValue);
}
}
}
}
inputStream.close();
}在上面的操作中可以看到POI對03版和07版excel的操作的代碼基本一致,只需修改文件的後綴和類名即可。
EasyExcel
EasyExcel是一個基於Java的簡單、省內存的讀寫Excel的開源項目。在儘可能節約內存的情況下支持讀寫百M的Excel。
easyExcel官網:https://www.yuque.com/easyexcel
導入依賴
<!-- 導入easyExcel的pom依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>創建對象
@Data
public class DemoData {
@ExcelProperty("字符串標題")
private String string;
@ExcelProperty("日期標題")
private Date date;
@ExcelProperty("數字標題")
private Double doubleData;
/**
* 忽略這個欄位
*/
@ExcelIgnore
private String ignore;
public void setString(String string) {
this.string = string;
}
public void setDate(Date date) {
this.date = date;
}
public void setDoubleData(Double doubleData) {
this.doubleData = doubleData;
}
}自動生成數據
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);
}寫入數據
調用EasyExcel中的write()方法
其中write()中有兩個參數,第一個參數是文件名,第二個參數是格式類
要想生成表要用到sheet()方法,sheet()方法中寫入一個字符串表示表的名字
要想將數據寫入表中還要調用doWrite()方法
格式如下:
EasyExcel.write(文件名, 類名.class).sheet(表明).doWrite(數據);@Test
public void simpleWrite() {
// 寫法1
String fileName = PATH + "easyTest.xlsx";
// 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
// write的兩個參數:文件名,格式類
// sheet生成表明
// doWrite寫入數據
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}執行後的結果
讀取數據
假設這個是你的DAO存儲
public class DemoDAO {
public void save(List<DemoData> list) {
}監聽器
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
*/
private DemoDAO demoDAO;
public DemoDataListener() {
demoDAO = new DemoDAO();
}
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 這個每一條數據解析都會來調用
* 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();
}
}
/**
* 所有數據解析完成了 都會來調用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裡也要保存數據,確保最後遺留的數據也存儲到資料庫
saveData();
LOGGER.info("所有數據解析完成!");
}
/**
* 加上存儲資料庫
*/
private void saveData() {
LOGGER.info("{}條數據,開始存儲資料庫!", list.size());
demoDAO.save(list);
LOGGER.info("存儲資料庫成功!");
}
}測試
@Test
public void simpleRead() {
String fileName = PATH + "easyTest.xlsx";
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}————————————————
版權聲明:本文為CSDN博主「慄山未來~」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本聲明。
原文連結:
https://blog.csdn.net/weixin_45890113/article/details/115742939
粉絲福利:Java從入門到入土學習路線圖
👇👇👇
感謝點讚支持下哈