Java操作Excel文件解析与读写方法详解
OlaiolaiO 人气:0一、概述
在应用程序的开发过程中,经常需要使用 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); } } }
加载全部内容