1 背景
项目中使用到了导入导出的功能所以自己写了一个关于POI的工具类和demo,希望能帮到会使用到的朋友。对比了很多市面上的方法,最后还是选择了easyExcel,也把easyExcel的demo也写了,文章底部会有相关跳转连接,跳转到POI的。
2 代码
2.1 依赖
easyExcel是封装了poi 所以还是会用到poi的依赖,这里是需要导入的
org.apache.poi
poi
4.1.2
org.apache.poi
poi-ooxml
4.1.2
com.alibaba
easyexcel
2.1.6
com.alibaba
fastjson
1.2.51
2.2 实体类
设立用到了ExcelProperty注解,可以简单理解成对应excel的表头名(相当于jpa的别名对应)。要注意的是这里还有一个Converter 这个待会也会讲解,是做为数据转换的,因为这里不支持Timestamp 各式,但是支持Date的,不过如果想输出样式改变,还是 要写Converter 的(比如想输入的是中文{1995年12月} 这种需要转换)。
public class DemoData {
private int id;
@ExcelProperty(value ="生效日期",converter = CustomNumberDateConverter.class)
private Timestamp date;
@ExcelProperty(value ="专业职级名称")
private String string;
@ExcelProperty(value ="描述")
private double doubleData;
}
2.3 Listener
这里可以理解成每一条数据进入后,我需要做什么,我这里就简单的做了一个List的收集,然后使得数据get都得到的是一个List的数据格式方面我自己业务开发,当然,你也可以在这里写每条都入库,或者如果数据量太大,可以写个垃圾清理缓存回收之类的来加快性能。
public class DemoDataListener extends AnalysisEventListener {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
List list = new ArrayList<>();
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
list.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
public List get() {
return list;
}
public void set(List list) {
this.list=list;
}
}
2.4 Converter
这里就很简单,就是一个数据格式的转换,这里是把Excel短时间转Timestamp,要注意,excel的时间是从1900年1月1日开始,和我们代码的时间不一样,java是从1970年开始的,所以这里转换的时候还需要加上时间差,不只是单纯的时间转换。convertToJavaData()为数据进入时的转换方法(导入)。convertToExcelData()为数据输出时的转换方法(导出)。
/**
* @className CustomNumberDateConverter
* @Description 自定义转换器(Excel短时间转Timestamp)
* @Author CabbageDevil
* @Date 2020/3/11 9:46
* @Version 1.0
**/
public class CustomNumberDateConverter implements Converter {
private static SimpleDateFormat sdf;
@Override
public Class supportJavaTypeKey() {
return Timestamp.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Timestamp convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
CellDataTypeEnum cellDataTypeEnum = cellData.getType();
if (CellDataTypeEnum.NUMBER.equals(cellDataTypeEnum)) {
if (null == cellData.getNumberValue()) {
return null;
}else if(1 >= cellData.getNumberValue().intValue()||73051<=cellData.getNumberValue().intValue()){
//用于校验日期填写不按规范
return new Timestamp(-1);
}
Calendar c = new GregorianCalendar(1900, 0, -1);
Date year1900 = c.getTime();
int numberValue = cellData.getNumberValue().intValue();
if (61 > numberValue) {
numberValue = numberValue + 1;
}
//42605是距离1900年1月1日的天数
Date newDate = DateUtils.addDays(year1900, numberValue);
return DateUtil.dataToTimestamp(newDate);
} else {//处理文本类型日期
if (sdf == null) {
sdf = new SimpleDateFormat("yyyy/MM/dd");
sdf.setLenient(false);
}
try {
String[] split = cellData.getStringValue().split("/");
if (split.length != 3) {
return new Timestamp(-1);//用于校验日期填写不按规范
}
String years = split[0];
//long y = Long.parseLong(years);
if (years.length() != 4) {//年份长度需要为4
return new Timestamp(-1);//用于校验日期填写不按规范
}
return new Timestamp(sdf.parse(cellData.getStringValue()).getTime());
} catch (Exception e) {
// logger.error("字符串转化为日期错误:", e);
return new Timestamp(-1);//用于校验日期填写不按规范
}
}
}
@Override
public CellData convertToExcelData(Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
if (null != value) {
if (sdf == null) {
sdf = new SimpleDateFormat("yyyy/MM/dd");
sdf.setLenient(false);
}
String dateStr = sdf.format(value);
return new CellData(dateStr);
} else {
return new CellData();
}
}
}
2.5 demo
这里的读,写了一个简单的读读取后会得到一个集合,也就是我们上面Listener的收容结果。
这里的写,是填充写,需要一个模板,模板样式如下:
相关的规则这里就不再阐述,可以查阅easyExcel的相关文档
public class easyexcelDemo {
@Test
public void simpleppRead() {
String filePath = "D:/test/新增专业职级.xlsx";
File file = new File(filePath);
DemoDataListener demoDataListener = new DemoDataListener();
List objects = EasyExcel.read(filePath, DemoData.class, demoDataListener).sheet().headRowNumber(4).doReadSync();
System.out.println(objects.toString());
}
@Test
public void listFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// 填充list 的时候还要注意 模板中{.} 多了个点 表示list
String templateFileName ="D:/test/新增专业职级.xlsx";
// 方案1 一下子全部放到内存里面 并填充
String fileName = "D:/test/" + System.currentTimeMillis() + ".xlsx";
// 这里 会填充到第一个sheet, 然后文件流会自动关闭
EasyExcel.write(fileName,DemoData.class).withTemplate(templateFileName).sheet().doFill(data());
}
private List data() {
List list = new ArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Timestamp(System.currentTimeMillis()));
data.setDoubleData(0.56+i);
list.add(data);
}
return list;
}
}
这里就完成了,简单的读和填充,基本可以满足日常开发。
poi实战工具类的地址:
https://blog.csdn.net/BCDMW233/article/details/105224181
easyExcel git 地址:
https://github.com/alibaba/easyexcel
easyExcel 文档地址
https://www.yuque.com/easyexcel/doc/easyexcel