当前位置:   article > 正文

Excel文件解析与超大Excel文件读写_workbook解析excel

workbook解析excel

一、概述

  • 在应用程序的开发过程中,经常需要使用 Excel 文件来进行数据的导入或导出。所以,在通过Java语言实现此 类需求的时候,往往会面临着Excel文件的解析(导入)或生成(导出)。
  • 在Java技术生态圈中,可以进行Excel文件处理的主流技术包括: Apache POI 、 JXL 、 Alibaba EasyExcel 等。

二、Apache POI

  • Apache POI 是用 Java 编写的免费开源的跨平台的 Java API , Apache POI 提供 给 Java 程序对 Microsoft Office 格式档案进行读写功能的 API 开源类库。

       它分别提供对不同格式文件的解析:

  • HSSF - 提供读写Microsoft Excel格式档案的功能。
  • XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。
  • HWPF - 提供读写Microsoft Word格式档案的功能。
  • HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
  • HDGF - 提供读写Microsoft Visio格式档案的功能。

三、XSSF解析Excel文件

  • HSSF 用于解析旧版本(*.xls)Excel文件,由于旧版本的Excel文件只能存在65535行数据,所以目前已经不常用。所以 目前主要采用 XSSF 进行新版本(*.xlsx)Exce文件的解析。

  1.Workbook(Excel文件)

  • Workbook 接口代表一个 Excel 文件,用于创建或加载(解析) Excel 文件。常见实现类是 XSSFWorkbook 。
  • 创建Excel文件

  1. try (Workbook workbook = new XSSFWorkbook();
  2. FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) {
  3. workbook.write(fos);
  4. } catch (IOException e) {
  5. e.printStackTrace();
  6. }
  • 解析Excel文件

  1. // 输入流
  2. FileInputStream fis = new FileInputStream("c:\\test\\1627356554991.xlsx");
  3. // Excel文件对象
  4. Workbook workbook = new XSSFWorkbook(fis);

   2.Sheet(工作簿)

  • 通过 Workbook 来进行工作簿 Sheet 对象的获取或创建
  • 创建工作簿

  1. // 按照默认名称创建工作簿
  2. Sheet sheet1 = workbook.createSheet();
  3. // 按照自定义名称创建工作簿
  4. Sheet sheet2 = workbook.createSheet("自定义工作簿2");
  • 获取工作簿

  1. // 按照工作簿下标获取Sheet
  2. Sheet sheet01 = workbook.getSheetAt(0);
  3. // 按照工作簿名称获取Sheet
  4. Sheet sheet02 = workbook.getSheet("Sheet0");
  • 获取工作簿的数量

int n = workbook.getNumberOfSheets();

3.Row(数据行)

  • 通过 Sheet 来进行数据行 Row 对象的获取或创建
  • 创建数据行

Row row = sheet.createRow(0);
  • 获取首行下标和尾行下标

  1. int first = sheet.getFirstRowNum();
  2. int last = sheet.getLastRowNum();
  • 根据下标获取指定行

Row row = sheet.getRow(0);
  • 遍历所有行

  1. for(Row row : sheet) {
  2. System.out.println(row);
  3. }
  • 遍历指定区域行

  1. for (int i = 1; i <= sheet.getLastRowNum(); i++) {
  2. Row row = sheet.getRow(i);
  3. System.out.println(row);
  4. }

4.Cell(单元格)

  • 通过 Row 来进行单元格 Cell 对象的获取或创建。
  • 创建单元格

Cell cell0 = row.createCell(0);
  • 设置单元格值

cell0.setCellValue(UUID.randomUUID().toString());
  • 根据下标获取单元格

Cell cell = row.getCell(1);
  • 遍历所有单元格

  1. for(Cell cell : row) {
  2. }
  • 获取单元格的类型

CellType type = cell.getCellType();
  • 设置单元格样式

  1. // 创建单元格样式
  2. DataFormat dataFormat = workbook.createDataFormat();
  3. Short formatCode = dataFormat.getFormat("yyyy-MM-dd HH:mm:ss");
  4. CellStyle cellStyle = workbook.createCellStyle();
  5. cellStyle.setDataFormat(formatCode);
  6. // 为当前行创建单元格
  7. Cell cell1 = row.createCell(1);
  8. cell1.setCellStyle(cellStyle); // 设置单元格样式
  9. cell1.setCellValue(new Date()); // 保存当前日期时间至本单元格
  • 设置单元格对齐

  1. // 创建单元格样式
  2. CellStyle cellStyle = workbook.createCellStyle();
  3. //设置单元格的水平对齐类型。 此时水平居中
  4. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  5. // 设置单元格的垂直对齐类型。 此时垂直靠底边
  6. cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);

四、超大Excel文件读写

1、使用POI写入

使用 SXSSFWorkbook 进行写入,通过设置 SXXFWorkbook 的构造参数,可以设置每次在内存中保持的行 数,当达到这个值的时候,那么会把这些数据 flush 到磁盘上,这样就不会出现内存不够的情况。

  1. try (Workbook workbook = new SXSSFWorkbook(100);
  2. FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) {
  3. Sheet sheet1 = workbook.createSheet();
  4. for (int i = 0; i <= 1000000; i++) {
  5. Row row = sheet1.createRow(i);
  6. Cell cell0 = row.createCell(0);
  7. cell0.setCellValue(UUID.randomUUID().toString());
  8. Cell cell1 = row.createCell(1);
  9. cell1.setCellValue(new Date());
  10. }
  11. workbook.write(fos);
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }

但是读取超大Excel时POI会把文件的所有内容都加载到内存中,很容易占用大量内存;甚至发生out of memory异常。

2、使用EasyExcel

  • Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
  • EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
  • EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理。

例:

  1. //准备实体类
  2. public class Order {
  3. @ExcelProperty("订单编号")
  4. private String orderId; // 订单编号
  5. @ExcelProperty("支付金额")
  6. @NumberFormat("¥#,###")
  7. private Double payment; // 支付金额
  8. @ExcelProperty(value = "创建日期",converter = LocalDateTimeConverter.class)
  9. private LocalDateTime creationTime; // 创建时间
  10. public Order() {
  11. this.orderId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmss"))
  12. + UUID.randomUUID().toString().substring(0, 5);
  13. this.payment = Math.random() * 10000;
  14. this.creationTime = LocalDateTime.now();
  15. }
  16. public String getOrderId() {
  17. return orderId;
  18. }
  19. public void setOrderId(String orderId) {
  20. this.orderId = orderId;
  21. }
  22. public Double getPayment() {
  23. return payment;
  24. }
  25. public void setPayment(Double payment) {
  26. this.payment = payment;
  27. }
  28. public LocalDateTime getCreationTime() {
  29. return creationTime;
  30. }
  31. public void setCreationTime(LocalDateTime creationTime) {
  32. this.creationTime = creationTime;
  33. }
  34. @Override
  35. public String toString() {
  36. return "Order [orderId=" + orderId + ", payment=" + payment + ", creationTime=" + creationTime + "]";
  37. }
  38. }
  1. //准备Converter转换类
  2. public class LocalDateTimeConverter implements Converter<LocalDateTime> {
  3. @Override
  4. public Class<LocalDateTime> supportJavaTypeKey() {
  5. return LocalDateTime.class;
  6. }
  7. @Override
  8. public CellDataTypeEnum supportExcelTypeKey() {
  9. return CellDataTypeEnum.STRING;
  10. }
  11. @Override
  12. public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
  13. GlobalConfiguration globalConfiguration) {
  14. return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  15. }
  16. @Override
  17. public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
  18. GlobalConfiguration globalConfiguration) {
  19. return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  20. }
  21. }
  • 写入数据

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import com.alibaba.excel.EasyExcel;
  4. public class Demo01 {
  5. public static void main(String[] args) {
  6. long begin = System.currentTimeMillis();
  7. // 写入100w
  8. EasyExcel.write("D:\\java.workspace\\1000W.xlsx", Order.class)
  9. .sheet("订单列表")
  10. .doWrite(data());
  11. long end = System.currentTimeMillis();
  12. System.out.println("共耗时"+(end-begin)+"毫秒");
  13. }
  14. // 创建100w条订单数据
  15. private static List<Order> data() {
  16. List<Order> list = new ArrayList<Order>();
  17. for (int i = 0; i < 1000000; i++) {
  18. list.add(new Order());
  19. }
  20. return list;
  21. }
  22. }

  •  读取数据

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Map;
  4. import com.alibaba.excel.EasyExcel;
  5. import com.alibaba.excel.context.AnalysisContext;
  6. import com.alibaba.excel.event.AnalysisEventListener;
  7. public class Demo02 {
  8. public static void main(String[] args) {
  9. //用于保存读取到的结果
  10. List<Order> orderList = new ArrayList<Order>();
  11. //读取
  12. EasyExcel.read("D:\\java.workspace\\1000W.xlsx", Order.class,new AnalysisEventListener<Order>() {
  13. @Override
  14. public void invoke(Order order, AnalysisContext arg1) {
  15. // 读取每条数据
  16. orderList.add(order);
  17. }
  18. @Override
  19. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
  20. // 读取到列头
  21. System.out.println(headMap);
  22. }
  23. @Override
  24. public void doAfterAllAnalysed(AnalysisContext arg0) {
  25. // 读取完毕
  26. System.out.println("END");
  27. }
  28. }).sheet().doRead();
  29. //遍历
  30. for(Order order : orderList) {
  31. System.out.println(order);
  32. }
  33. }
  34. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/88858
推荐阅读
相关标签
  

闽ICP备14008679号