1.使用POI写入
当我们想在Excel文件中写入100w条数据时,使用XSSFWorkbook进行写入时会发现,只有将100w条数据全部加载到内存后才会用write()方法统一写入,效率很低,所以我们引入了SXXFWorkbook进行超大Excel文件读写。
通过设置 SXXFWorkbook 的构造参数,可以设置每次在内存中保持的行数,当达到这个值的时候,那么会把这些数据flush 到磁盘上,这样就不会出现内存不够的情况。
package com.ztt.Demo02;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
public class demo07 {
public static void main(String[] args) {
try (SXSSFWorkbook workbook = new SXSSFWorkbook(100);
FileOutputStream fos = new FileOutputStream("D:\\test\\tt\\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();
}
}
}
2.使用EasyExcel写入
使用EasyExcel,我们首先要导入相关jar包
准备一个普通的Order类:
public class Order {
private String orderId;
private Double payment;
public Order() {
this.orderId=LocalDateTime.now().
format(DateTimeFormatter.ofPattern(
"yyyyMMddHHmmss"))+UUID.randomUUID()
.toString().substring(0,5);
this.payment=Math.random()*1000;
}
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;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", payment="
+ payment + "]";
}
}
我们发现,Order类中的成员变量名就是我们生成的Excel文件中的列头。那么如果我们想自定义列头时,我们可以用:@ExcelProperty("列头名")
然后我们来通过EasyExcel来将100w条数据写入excel文件:
package com.ztt.demo01;
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 Test1 {
public static void main(String[] args) {
//将100w条订单数据写入Excel文件
// EasyExcel.write("D:\\test\\poi\\alibaba\\0421-a.xlsx",Order.class)
// .sheet("订单数据")
// .doWrite(createOrderData());
//读取Excel文件中的数据
//参数1:文件的的路径
//参数2:文件中每条数据对应的Class类型
//参数3:如何解析
EasyExcel.read("D:\\test\\poi\\alibaba\\0421-a.xlsx",Order.class,new AnalysisEventListener<Order>() {
//读取列头
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("Excel文件的列头:"+headMap);
}
//处理每行数据
@Override
public void invoke(Order order, AnalysisContext arg1) {
System.out.println("读取到一条订单数据:"+order);
}
@Override
public void doAfterAllAnalysed(AnalysisContext arg0) {
System.out.println("读取完毕");
}
}).sheet().doRead();
}
//生成100w条订单数据
private static List<Order> createOrderData() {
List<Order> orderList=new ArrayList<Order>();
for(int i=0;i<1000;i++) {
orderList.add(new Order());
}
return orderList;
}
}
当我们想加入一列日期数据时:
public class Order {
@ExcelProperty("订单编号")
private String orderId;
@ExcelProperty("支付金额")
private Double payment;
@ExcelProperty("创建时间")
private LocalDateTime creatTime;
public Order() {
this.orderId=LocalDateTime.now().
format(DateTimeFormatter.ofPattern(
"yyyyMMddHHmmss"))+UUID.randomUUID()
.toString().substring(0,5);
this.payment=Math.random()*1000;
this.creatTime=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 getCreatTime() {
return creatTime;
}
public void setCreatTime(LocalDateTime creatTime) {
this.creatTime = creatTime;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", payment=" + payment
+ ", creatTime=" + creatTime + "]";
}
}
运行结果:
通过阅读报错提示(Can not find 'Converter' support class LocalDateTime.) ,我们大概可以知道,是因为找不到一个支持LocalDateTime类的转换器,所以为了解决这个问题,我们可以自己写一个比较器类:
package com.ztt.demo01;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class LocalDateTimeConverter implements Converter<LocalDateTime>{
//Excel文件中的类型
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
//程序中的类型
@Override
public Class supportJavaTypeKey() {
return LocalDateTime.class;
}
//将LocalDateTime类型的数据转换成String类型的数据
//并封装至一个Excel文件中的ellData
@Override
public CellData convertToExcelData(LocalDateTime value, ExcelContentProperty arg1, GlobalConfiguration arg2)
throws Exception {
return new CellData<>(
value.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")));
}
//从ce1lData中获取一个String类型的致据
//并转换成LocalDateTime类型
@Override
public LocalDateTime convertToJavaData(CellData cellData ,ExcelContentProperty arg1, GlobalConfiguration arg2)
throws Exception {
return LocalDateTime.parse(cellData.getStringValue(),DateTimeFormatter.ofPattern( "yyyy年MM月dd日 HH:mm:ss"));
}
}
当我们写好这个比较器后,就需要给成员变量creatTime显示的设置好比较器:
package com.ztt.demo01;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.NumberFormat;
public class Order {
@ExcelProperty("订单编号")
private String orderId;//订单编号
@ExcelProperty("支付金额")
@NumberFormat("¥#,###")
private Double payment;//支付金额
//设置LocalDateTime对应转换器
@ExcelProperty(value="创建时间",converter=LocalDateTimeConverter.class)
private LocalDateTime createionTime;//创建日期
public Order() {
this.orderId=LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHss"))+UUID.randomUUID().toString().substring(0,5);
this.payment=Math.random()*10000;
this.createionTime=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 getCreateionTime() {
return createionTime;
}
public void setCreateionTime(LocalDateTime createionTime) {
this.createionTime = createionTime;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", payment=" + payment + ", createionTime=" + createionTime + "]";
}
}
运行结果:
练习:检查Excel文件
1.序号是否连续
2.检查性别是否为男或女
3.身份证号
3.1 身份证号码格式(必须为18位)
3.2 身份证号码不能重复
3.3 身份证号码开头两位是否与籍贯符合
北京 11 天津12 河北 13 山西14 内蒙古 15
陕西61 甘肃62 青海 63
4.学历只能填写:大专、本科、硕士、其它
5.体重在40-120之间
package com.ztt.Demo02;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class demo05 {
public static void main(String[] args) {
List<String> errorMsgList = validateDataExcel("D:\\test\\tt\\demo-data.xlsx");
if (errorMsgList.size() == 0) {
System.out.println("文件检查无误!");
} else {
// 显示所有错误信息
for (String err : errorMsgList) {
System.out.println(err);
}
}
}
public static List<String> validateDataExcel(String excelDataFilePath) {
// 1.序号是否连续
// 2.检查性别是否为男或女
// 3.身份证号
// 3.1 身份证号码格式(必须为18位)
// 3.2 身份证号码不能重复
// 3.3 身份证号码开头两位是否与籍贯符合
// 北京 11 天津12 河北 13 山西14 内蒙古 15
// 陕西61 甘肃62 青海 63
// 4.学历只能填写:大专、本科、硕士、其它
// 5.体重在40-120之间
//创建用于保存错误提示信息的集合
ArrayList<String> errorList=new ArrayList<String>();
//创建用于检查身份证号码是否重复的set集合
HashSet<String> idCardNoSet = new HashSet<String>();
//创建用于检查身份证号码前2位与籍贯省份之间的映射关系的Map集合
//北京11天津12河北13山西14内蒙古15
//陕西61甘肃62青海63
HashMap<String,String> provinceMap = new HashMap<String,String>() {
//构造代码块
{
put("11","北京");
put("12","天津");
put( "13","河北");
put("14","山西");
put("15","内蒙古");
put("61","陕西");
put("62","甘肃");
put("63","青海");
}
};
//创建一个用于检查学历的List集合
List<String> eduList = Arrays.asList("大专", "本科" , "硕士","其它");
try(Workbook workbook=new XSSFWorkbook(excelDataFilePath)){
Sheet sheet=workbook.getSheetAt(0);
for(int i=1;i<sheet.getLastRowNum();i++) {
Row row =sheet.getRow(i);
//1.序号是否连续
Cell cellId=row.getCell(0);
int rowNum=row.getRowNum();
int id=(int)cellId.getNumericCellValue();
if(rowNum!=id) {
errorList.add(String.format("%d行的编号不连续!", rowNum));
}
//2.检查性别是否为男或女
String gender=row.getCell(2).getStringCellValue();
if(!gender.equals("男") && !gender.equals("女")) {
errorList.add(String.format("%d行的性别有误!", rowNum));
}
//3.身份证号
String idCardNo = row.getCell(3).getStringCellValue();
//3.1身份证号码格式(必须为18位)
if(idCardNo.length() != 18) {
errorList.add(String.format("%d行的身份证号码长度有误! ",rowNum));
}
//3.2身份证号码不能重复
if(!idCardNoSet.add(idCardNo)) {
errorList.add(String.format("%d行的身份证号码重复存在! ",rowNum));
}
//3.3身份证号码开头两位是否与籍贯符合
//北京11天津12河北13山西14内蒙古15
//陕西61甘肃62青海63
String idCardNoHomeCode = idCardNo.substring(0,2);
String homeValue = provinceMap.get(idCardNoHomeCode); //根据身份证号码前两位获取正确的籍贯省份名称
String home = row.getCell(6).getStringCellValue();//获取表格中当前行的籍贯信息
if(!homeValue.equals( home)) {
errorList.add(String.format("%d行的身份证籍贯信息不一致! ",rowNum));
}
//4.学历只能填写:大专、本科、硕士、其它
String eduValue=row.getCell(7).getStringCellValue();
if(!eduList.contains(eduValue)) {
errorList.add(String.format("%d行的学历信息不符合规范! ",rowNum));
}
}
} catch (IOException e) {
e.printStackTrace();
}
return errorList;
}
}
运行结果:
1行的学历信息不符合规范!
3行的身份证籍贯信息不一致!
4行的编号不连续!
5行的学历信息不符合规范!
7行的身份证号码长度有误!
9行的身份证号码长度有误!
9行的学历信息不符合规范!
10行的身份证籍贯信息不一致!
11行的身份证号码长度有误!
12行的编号不连续!
13行的身份证号码长度有误!
14行的学历信息不符合规范!
15行的学历信息不符合规范!
16行的性别有误!
18行的编号不连续!
22行的身份证籍贯信息不一致!
24行的身份证号码重复存在!
25行的性别有误!
33行的身份证号码重复存在!
36行的身份证号码长度有误!
39行的身份证籍贯信息不一致!
41行的身份证籍贯信息不一致!
43行的身份证籍贯信息不一致!
44行的身份证籍贯信息不一致!
46行的身份证籍贯信息不一致!