Java操作Excel文件解析与读写方法详解
作者:OlaiolaiO
一、概述
在应用程序的开发过程中,经常需要使用 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文件
try (Workbook workbook = new XSSFWorkbook(); FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) { workbook.write(fos); } catch (IOException e) { e.printStackTrace(); }
解析Excel文件
// 输入流 FileInputStream fis = new FileInputStream("c:\\test\\1627356554991.xlsx"); // Excel文件对象 Workbook workbook = new XSSFWorkbook(fis);
2.Sheet(工作簿)
通过 Workbook 来进行工作簿 Sheet 对象的获取或创建
创建工作簿
// 按照默认名称创建工作簿 Sheet sheet1 = workbook.createSheet(); // 按照自定义名称创建工作簿 Sheet sheet2 = workbook.createSheet("自定义工作簿2");
获取工作簿
// 按照工作簿下标获取Sheet Sheet sheet01 = workbook.getSheetAt(0); // 按照工作簿名称获取Sheet Sheet sheet02 = workbook.getSheet("Sheet0");
获取工作簿的数量
int n = workbook.getNumberOfSheets();
3.Row(数据行)
通过 Sheet 来进行数据行 Row 对象的获取或创建
创建数据行
Row row = sheet.createRow(0);
获取首行下标和尾行下标
int first = sheet.getFirstRowNum(); int last = sheet.getLastRowNum();
根据下标获取指定行
Row row = sheet.getRow(0);
遍历所有行
for(Row row : sheet) { System.out.println(row); }
遍历指定区域行
for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); System.out.println(row); }
4.Cell(单元格)
通过 Row 来进行单元格 Cell 对象的获取或创建。
创建单元格
Cell cell0 = row.createCell(0);
设置单元格值
cell0.setCellValue(UUID.randomUUID().toString());
根据下标获取单元格
Cell cell = row.getCell(1);
遍历所有单元格
for(Cell cell : row) {}
获取单元格的类型
CellType type = cell.getCellType();
设置单元格样式
// 创建单元格样式 DataFormat dataFormat = workbook.createDataFormat(); Short formatCode = dataFormat.getFormat("yyyy-MM-dd HH:mm:ss"); CellStyle cellStyle = workbook.createCellStyle(); cellStyle.setDataFormat(formatCode); // 为当前行创建单元格 Cell cell1 = row.createCell(1); cell1.setCellStyle(cellStyle); // 设置单元格样式 cell1.setCellValue(new Date()); // 保存当前日期时间至本单元格
设置单元格对齐
// 创建单元格样式 CellStyle cellStyle = workbook.createCellStyle(); //设置单元格的水平对齐类型。 此时水平居中 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 设置单元格的垂直对齐类型。 此时垂直靠底边 cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
四、超大Excel文件读写
1.使用POI写入
使用 SXSSFWorkbook 进行写入,通过设置 SXXFWorkbook 的构造参数,可以设置每次在内存中保持的行 数,当达到这个值的时候,那么会把这些数据 flush 到磁盘上,这样就不会出现内存不够的情况。
try (Workbook workbook = new SXSSFWorkbook(100); FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) { Sheet sheet1 = workbook.createSheet(); for (int i = 0; i <= 1000000; i++) { Row row = sheet1.createRow(i); Cell cell0 = row.createCell(0); cell0.setCellValue(UUID.randomUUID().toString()); Cell cell1 = row.createCell(1); cell1.setCellValue(new Date()); } workbook.write(fos); } catch (IOException e) { e.printStackTrace(); }
但是读取超大Excel时POI会把文件的所有内容都加载到内存中,很容易占用大量内存;甚至发生out of memory异常。
2.使用EasyExcel
- Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
- EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
- EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理。
例:
//准备实体类 public class Order { @ExcelProperty("订单编号") private String orderId; // 订单编号 @ExcelProperty("支付金额") @NumberFormat("¥#,###") private Double payment; // 支付金额 @ExcelProperty(value = "创建日期",converter = LocalDateTimeConverter.class) private LocalDateTime creationTime; // 创建时间 public Order() { this.orderId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmss")) + UUID.randomUUID().toString().substring(0, 5); this.payment = Math.random() * 10000; this.creationTime = LocalDateTime.now(); } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public Double getPayment() { return payment; } public void setPayment(Double payment) { this.payment = payment; } public LocalDateTime getCreationTime() { return creationTime; } public void setCreationTime(LocalDateTime creationTime) { this.creationTime = creationTime; } @Override public String toString() { return "Order [orderId=" + orderId + ", payment=" + payment + ", creationTime=" + creationTime + "]"; } }
//准备Converter转换类 public class LocalDateTimeConverter implements Converter<LocalDateTime> { @Override public Class<LocalDateTime> supportJavaTypeKey() { return LocalDateTime.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } @Override public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } }
写入数据
import java.util.ArrayList; import java.util.List; import com.alibaba.excel.EasyExcel; public class Demo01 { public static void main(String[] args) { long begin = System.currentTimeMillis(); // 写入100w EasyExcel.write("D:\\java.workspace\\1000W.xlsx", Order.class) .sheet("订单列表") .doWrite(data()); long end = System.currentTimeMillis(); System.out.println("共耗时"+(end-begin)+"毫秒"); } // 创建100w条订单数据 private static List<Order> data() { List<Order> list = new ArrayList<Order>(); for (int i = 0; i < 1000000; i++) { list.add(new Order()); } return list; } }
读取数据
import java.util.ArrayList; import java.util.List; import java.util.Map; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; public class Demo02 { public static void main(String[] args) { //用于保存读取到的结果 List<Order> orderList = new ArrayList<Order>(); //读取 EasyExcel.read("D:\\java.workspace\\1000W.xlsx", Order.class,new AnalysisEventListener<Order>() { @Override public void invoke(Order order, AnalysisContext arg1) { // 读取每条数据 orderList.add(order); } @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { // 读取到列头 System.out.println(headMap); } @Override public void doAfterAllAnalysed(AnalysisContext arg0) { // 读取完毕 System.out.println("END"); } }).sheet().doRead(); //遍历 for(Order order : orderList) { System.out.println(order); } } }
到此这篇关于Java操作Excel文件解析与读写方法详解的文章就介绍到这了,更多相关Java Excel文件解析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!