前言
文章目录
- 前言
- JExcelAPI
- Demo
- POI
- HSSFWorkBook
- XSSFWorkBook
- Demo
- SXSSFWorkBook
- Demo
- XSSFReader
- Demo
- EasyExcel
- Demo
demo代码:https://github.com/RwTo/excel-demo
JAVA解析Excel 一般有三种方式
JExcelAPI
POI
EasyExcel
JExcelAPI
官网:https://jexcelapi.sourceforge.net/
- 仅支持 2003 版本的Excel 也就是 后缀名为 xls 的文件
- 采用流式处理模型,逐行读取和写入 ——因此 可以处理大量数据,一般不会出现OOM
Demo
<!--JExcelAPI--><dependency><groupId>net.sourceforge.jexcelapi</groupId><artifactId>jxl</artifactId><version>2.6.12</version></dependency>
读xls 文件
public class ReadExcelDemo {public static void main(String[] args) {try {// 1. 打开 Excel 文件String filePath = ExcelConstant.EXCEL_PATH_XLS;Workbook workbook = Workbook.getWorkbook(new java.io.File(filePath));// 2. 获取第一个工作表Sheet sheet = workbook.getSheet(0);/*** -Xms64m -Xmx64m* row = 4000 col = 50* 正常读*/// 3. 遍历每一行,并读取数据for (int row = 0; row < sheet.getRows(); row++) {for (int col = 0; col < sheet.getColumns(); col++) {Cell cell = sheet.getCell(col, row);String content = cell.getContents();content += cell.getCellFormat().getBackgroundColour().getDescription();System.out.print(content+"\t");}System.out.println();}// 4. 关闭工作簿workbook.close();} catch (Exception e) {e.printStackTrace();}}
}
写xls 文件
public class WriteExcelDemo {public static void main(String[] args) {try {String filePath = ExcelConstant.EXCEL_PATH_XLS;// 1. 创建工作簿WritableWorkbook workbook = Workbook.createWorkbook(new File(filePath));// 2. 创建工作表WritableSheet sheet = workbook.createSheet("Sheet1", 0);// 3. 定义单元格颜色WritableCellFormat greenFormat = new WritableCellFormat();greenFormat.setBackground(jxl.format.Colour.GREEN);WritableCellFormat yellowFormat = new WritableCellFormat();yellowFormat.setBackground(jxl.format.Colour.YELLOW);/*** -Xms64m -Xmx64m* row = 4000 col = 50* 正常写*/// 4. 写入数据for (int row = 0; row < 4000; row++) {for (int col = 0; col < 50; col++) {if(col%2 == 0){Label label = new Label(col, row, "Cell " + (row + 1) + "-" + (col + 1),yellowFormat);sheet.addCell(label);}else{Label label = new Label(col, row, "Cell " + (row + 1) + "-" + (col + 1),greenFormat);sheet.addCell(label);}}}// 5. 保存工作簿workbook.write();workbook.close();} catch (Exception e) {e.printStackTrace();}}
}
POI
官网:https://poi.apache.org/
功能比较丰富,使用较为广泛,问题也比较多(比如OOM)
四种API 操作Excel
官网介绍:https://poi.apache.org/components/spreadsheet/how-to.html
文档对象模型
HSSFWorkBook (功能丰富,但会OOM)
XSSFWorkBook (功能丰富,但易OOM)
事件处理模型(流式处理)
SXSSFWorkBook (只支持xlsx 的写入)
XSSFReader(仅提供模板,需要使用者自己编写)
POM 文件
<!-- Apache POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency>
HSSFWorkBook
- 支持 xls 文件的解析和写入
- 基于DOM ,将整个 Excel 文件加载到内存中,构建一个完整的 Excel 文档对象模型树,再进行解析和操作,当文件较大时,可能会出现内存溢出
但因为 xls 文件支持的样式和单元格数量较少,一般不会出现OOM
XSSFWorkBook
- 支持 xlsx 文件的解析和写入
- 基于DOM ,将整个 Excel 文件加载到内存中,构建一个完整的 Excel 文档对象模型树,再进行解析和操作,当文件较大时,可能会出现内存溢出
Demo
写入excel(xls,xlsx)
public class WriteExcelDemo {public static void main(String[] args) {try {String filePath = ExcelConstant.EXCEL_PATH_XLS;//String filePath = ExcelConstant.EXCEL_PATH_XLSX;//Workbook workbook = new XSSFWorkbook(); //支持xlsx文件的写入Workbook workbook = new HSSFWorkbook(); //支持xls文件的写入Sheet sheet = workbook.createSheet("Sheet1");/*** -Xms64m -Xmx64m* row = 4000 col = 50* 出现OOM*/for (int row = 0; row < 4000; row++) {Row excelRow = sheet.createRow(row);for (int col = 0; col < 50; col++) {Cell cell = excelRow.createCell(col);cell.setCellValue("Cell " + (row + 1) + "-" + (col + 1));}}FileOutputStream fileOutputStream = new FileOutputStream(filePath);workbook.write(fileOutputStream);fileOutputStream.close();workbook.close();} catch (Exception e) {e.printStackTrace();}}
}
读excel(xls,xlsx)
public class ReadExcelDemo {public static void main(String[] args) {try {String filePath = ExcelConstant.EXCEL_PATH_XLSX;//String filePath = ExcelConstant.EXCEL_PATH_XLSX;FileInputStream fileInputStream = new FileInputStream(filePath);//WorkbookFactory会根据文件类型自动选择使用HSSFWorkBook 或 XSSFWorkBookWorkbook workbook = WorkbookFactory.create(fileInputStream);Sheet sheet = workbook.getSheetAt(0);/*** -Xms64m -Xmx64m* row = 4000 col = 50* 出现OOM*/for (Row row : sheet) {for (Cell cell : row) {String cellValue = getCellValueAsString(cell);System.out.print(cellValue + "\t");}System.out.println();}fileInputStream.close();workbook.close();} catch (Exception e) {e.printStackTrace();}}private static String getCellValueAsString(Cell cell) {if (cell == null) {return "";}switch (cell.getCellType()) {case STRING:return cell.getStringCellValue();case NUMERIC:return String.valueOf(cell.getNumericCellValue());case BOOLEAN:return String.valueOf(cell.getBooleanCellValue());default:return "";}}
}
SXSSFWorkBook
- 仅支持xlsx 的写入
- 基于流式处理,逐行对excel 处理,用磁盘空间换取内存,可以高效地写入数据到 Excel 文件中,同时减少内存占用。
- SXSSFWorkbook 采用了内存优化的设计,避免了将所有数据加载到内存的需求。它仅将有限数量的行缓冲在内存中,写入到输出流后即丢弃缓冲的数据,从而减少了内存占用。
- SXSSFWorkbook 支持设置在写入数据之前保留在内存中的行数。通过控制分页大小,可以灵活地控制内存使用,特别适合处理超大型的 Excel 文件。
Demo
写入xlsx
public class SXSSFWriteExcelDemo {public static void main(String[] args) {try {// Set custom temporary directoryString customTempDirPath = "temp";System.setProperty("java.io.tmpdir", customTempDirPath);String filePath = ExcelConstant.EXCEL_PATH_XLSX_BIG;SXSSFWorkbook workbook = new SXSSFWorkbook(500);//rowAccessWindowSize为内存中缓存的记录数,默认100Sheet sheet = workbook.createSheet("Sheet1");for (int row = 0; row < 100000; row++) {Row excelRow = sheet.createRow(row);for (int col = 0; col < 3; col++) {Cell cell = excelRow.createCell(col);cell.setCellValue("Cell " + (row + 1) + "-" + (col + 1));}}FileOutputStream fileOutputStream = new FileOutputStream(filePath);workbook.write(fileOutputStream);fileOutputStream.close();//使用 dispose() 方法释放(删除) SXSSFWorkbook 使用的临时资源,特别是在写入大量数据后,这一步骤很重要。workbook.dispose();/*如果不手动释放,默认等虚拟机停止也会删除临时文件poi.keep.tmp.files 通过这个配置可以控制虚拟机停止时,不删除临时文件*///程序执行结束后,手动删除临时文件目录(看是否需要)deleteTempFiles(new File(customTempDirPath));} catch (Exception e) {e.printStackTrace();}}private static void deleteTempFiles(File directory) {if (directory.isDirectory()) {File[] files = directory.listFiles();if (files != null) {for (File file : files) {deleteTempFiles(file);}}}if (!directory.delete()) {System.err.println("Failed to delete temp file: " + directory.getAbsolutePath());} else {System.out.println("Deleted temp file: " + directory.getAbsolutePath());}}}
XSSFReader
官网案例:https://poi.apache.org/components/spreadsheet/how-to.html#xssf_sax_api
- 仅支持 xlsx 的解析
- 基于流式处理,逐行读取单元格,不会将整个excel 存到内存,适合大型excel的处理
Demo
读xlsx
public class SAXWriteExcelDemo {public static void main(String[] args) throws Exception {String filePath = ExcelConstant.EXCEL_PATH_XLSX_BIG;InputStream is = new FileInputStream(filePath);OPCPackage opcPackage = OPCPackage.open(is);try {// 读取 Excel 文件XSSFReader reader = new XSSFReader(opcPackage);// 使用事件处理器处理 Sheet 数据SAXSheetHandler sheetHandler = new SAXSheetHandler();XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(reader.getStylesTable(), reader.getSharedStringsTable(), sheetHandler, false);XMLReader sheetParser = XMLReaderFactory.createXMLReader();sheetParser.setContentHandler(xmlHandler);Iterator<InputStream> sheetsData = reader.getSheetsData();while(sheetsData.hasNext()){InputStream sheetIs = sheetsData.next();InputSource sheet = new InputSource(sheetIs);//开始解析sheet页System.out.println("=====================================开始处理sheet页==============================================");long start = System.currentTimeMillis();sheetParser.parse(sheet);System.out.println("=====================================处理sheet结束==============================================");long end = System.currentTimeMillis();List<List<SAXCell>> curSheet = sheetHandler.getCurSheet();curSheet.forEach(System.out::println);System.out.println("处理行数:"+sheetHandler.getRowCount());System.out.println("处理单元格数:"+sheetHandler.getCellCount());System.out.println("处理时间(ms):"+(end-start));sheetHandler.clear();}} catch (IOException e) {e.printStackTrace();} catch (OpenXML4JException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();}finally {// 关闭输入流和 OPCPackageis.close();opcPackage.close();}}
}class SAXSheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {//当前页private List<List<SAXCell>> curSheet;//当前行private List<SAXCell> curRow;//总单元格数private int cellCount;public SAXSheetHandler() {this.curSheet = new LinkedList<>();}/*** 一行处理开始时** @param i 行号*/@Overridepublic void startRow(int i) {//开始处理新的一行,初始化当前行curRow = new LinkedList<>();}/*** 一行处理结束时** @param i 行号*/@Overridepublic void endRow(int i) {//一行处理结束,将这一行数据存入sheetcurSheet.add(curRow);}/*** 处理单元格(不会读取空单元格)** @param cellReference 单元格名称* @param formattedValue 单元格值* @param xssfComment 批注*/@Overridepublic void cell(String cellReference, String formattedValue, XSSFComment xssfComment) {SAXCell cell = new SAXCell(cellReference, formattedValue);curRow.add(cell);cellCount++;}@Overridepublic void headerFooter(String text, boolean isHeader, String tagName) {// 头部和页脚}@Overridepublic void endSheet() {//sheet处理结束}public List<List<SAXCell>> getCurSheet() {return curSheet;}public int getCellCount() {return cellCount;}public int getRowCount() {return curSheet.size();}public void clear(){curSheet = new LinkedList<>();cellCount = 0;}
}
@Data
class SAXCell {private String cellName;private String value;public SAXCell(String cellName, String value) {this.cellName = cellName;this.value = value;}
}
EasyExcel
官网:https://easyexcel.opensource.alibaba.com/
- 支持xlsx和xls 文件的解析和写入
- 流式处理,节省内存,可以处理大型excel
- 支持注解,简单易用
Demo
POM
<!--easyExcel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.0-beta2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
实例
@Data
public class UserData {private String name;private int age;private String email;public UserData() {}public UserData(String name, int age, String email) {this.name = name;this.age = age;this.email = email;}
}
写入excel(xls,xlsx)
public class EasyWriteExcelDemo {public static void main(String[] args) {String filePath = ExcelConstant.EE_EXCEL_PATH_XLSX_BIG;// 创建写入的数据列表List<UserData> dataList = new ArrayList<>();dataList.add(new UserData("Alice", 25, "alice@example.com"));dataList.add(new UserData("Bob", 30, "bob@example.com"));dataList.add(new UserData("Charlie", 28, "charlie@example.com"));// 使用 EasyExcel 写入 Excel 文件EasyExcel.write(filePath, UserData.class).sheet("Sheet1").doWrite(dataList);}
}
读取excel(xls,xlsx)
public class EasyReadExcelDemo {public static void main(String[] args) {String filePath = ExcelConstant.EE_EXCEL_PATH_XLSX_BIG;// 使用 EasyExcel 读取 Excel 文件EasyExcel.read(filePath, UserData.class, new UserDataListener()).sheet().doRead();}public static class UserDataListener extends AnalysisEventListener<UserData> {private List<UserData> dataList = new ArrayList<>();@Overridepublic void invoke(UserData data, AnalysisContext context) {dataList.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 在这里可以对 dataList 中的数据进行处理,比如保存到数据库或其他操作for (UserData userData : dataList) {System.out.println(userData.getName() + "\t" + userData.getAge() + "\t" + userData.getEmail());}}}
}