EasyExcel 数值型和最后一列超出文本框问题

Alibaba出品的EasyExcel对比原版excel库POI,极大的简化了开发人员的代码编写工作。EasyExcel使用注解和监听器等简化读写开发,

问题说明

在我的日常工作中,很大一部分的工作都是将数据库的数据整理归纳后导出为Excel,当然会碰到许许多多的问题,比如导出超时(当然这个问题大多数都是SQL执行的问题,日常几十万条数据的excel并不会花费多少时间),字段类型与需求不匹配、文本超出单元格、动态头…..

这里主要记录和说明其中几个问题

  • 字段类型不匹配
  • 最后一列文本内容超出单元格

字段类型不匹配

解决方法可看easyexcel 官方github Issues :https://github.com/alibaba/easyexcel/issues/2409

问题展示

如上图所示,当我们设置实体类的属性为字符串型时,默认导出的时候数值型会模糊单元格的格式,所以上方有个三角符号,这时候excel是无法进行数值的求和汇总等运算的。

分析

我们看一下导出excel时的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class Student implements Serializable {

@ExcelProperty( "学生编号")
private String id;

@ExcelProperty( "学生姓名")
private String name;

@ExcelProperty( "学生生日")
@DateTimeFormat("yyyy-mm-dd")
private Date birthday;

@ExcelProperty("学号")
private String stuNo;
}

注意 private String stuNo;为String类型,这里就是导致导出excel无法确定单元格格式的主要原因。

解决方法

修改方式很简单,将String改为对应的数值型格式即可如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class Student implements Serializable {

@ExcelProperty( "学生编号")
private String id;

@ExcelProperty( "学生姓名")
private String name;

@ExcelProperty( "学生生日")
@DateTimeFormat("yyyy-mm-dd")
private Date birthday;

@ExcelProperty("学号")
private Integer stuNo;
}

结果

PS: 第一列仍为String类型,所以导出仍有三角符号。

最后一列文本内容超出单元格

这个问题我在issues 中没有找到,可能问题比较简单:joy::joy::joy:

先说一下思路,构造出一列空值覆盖前一列内容超出部分,再手动生成标题头

问题

如果最后一列导出为长文本字符型时,很容易超出单元格,观感不佳,现象如下。

分析

我们看一下实体类多加了一个note字段,这个也就是上方长文本的字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Data
public class Student implements Serializable {

@ExcelProperty( "学生编号")
private String id;

@ExcelProperty( "学生姓名")
private String name;

@ExcelProperty( "学生生日")
@DateTimeFormat("yyyy-mm-dd")
private Date birthday;

@ExcelProperty("学号")
private Integer stuNo;

@ExcelProperty("备注")
private String note;
}

解决方法

我们首先看一下导出代码

1
2
3
4
EasyExcel.write("学生信息表.xlsx", Student.class)
.sheet()
.head(Student.class)
.doWrite(WriteDemo.getData());

注意一下其中的方法和参数write()和head()

write()重载方法入参

  • File file
  • File file,@Nullable Class head
  • String pathName
  • String pathName, @Nullable Class head
  • outputStream outputStream
  • outputStream outputStream, @Nullable Class head

所有的重载方法入参第一个都是确定文件名称(File )和位置(pathName),outputStream 是web上传、下载。我们关注后面一个参数 Class head这里指定了导出文件的head,同head方法功能相似,也就是说,这里我们去到 .head(Student.class)或者.write("学生信息表.xlsx", Student.class)中的Student.class都是正常的。

简单分析一下源代码
write
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class EasyExcelFactory {
public static ExcelWriterBuilder write(String pathName, Class head) {
ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
excelWriterBuilder.file(pathName);
if (head != null) {
excelWriterBuilder.head(head);
}

return excelWriterBuilder;
}
}


public abstract class AbstractParameterBuilder<T extends AbstractParameterBuilder, C extends BasicParameter> {


public T head(Class clazz) {
this.parameter().setClazz(clazz);
return this.self();
}
}

我们可以看到这里给excelWriterBuilder设置了一个head然后调用了AbstractParameterBuilder的head方法

1
2
3
4
5
6
7
8
9
10
public abstract class AbstractParameterBuilder<T extends AbstractParameterBuilder, C extends BasicParameter> {
/***/
public T head(Class clazz) {
this.parameter().setClazz(clazz);
return this.self();
}
/***/
protected abstract C parameter();
/***/
}

这里给 this.parameter()设置了类名,这里的this.parameter指的是AbstractParameterBuilder 中的protected abstract C parameter();方法,我们可以看到,这里其实本质上是调用了同一个方法。

head的特别之处

head方法本质上是用来动态生成头文件的,详情可参照官方文档https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write#%E5%8A%A8%E6%80%81%E5%A4%B4%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E5%A4%B4%E5%86%99%E5%85%A5

如果我们再write方法上没有指定标题头,则可以手动通过head方法构造,这样我们就可以实现最后一列不出单元格了

具体实现

先修改导出实体类添加一个占位符并赋初始值为空注意,如果没有赋值为空字符串,则不会写入excel进行覆盖前一列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
public class Student implements Serializable {

@ExcelProperty( "学生编号")
private String id;

@ExcelProperty( "学生姓名")
private String name;

@ExcelProperty( "学生生日")
@DateTimeFormat("yyyy-mm-dd")
private Date birthday;

@ExcelProperty("学号")
private Integer stuNo;

@ExcelProperty("备注")
private String note;

// 占位符
private String palaceHolder = "";
}

手动构造头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private List<List<String>> setHead() {

List<String> list = Arrays.asList(
"学生编号",
"学生姓名",
"学生生日",
"学号",
"备注"
);

return list.stream()
.map(Arrays::asList)
.collect(Collectors.toList());

}

最后生成excel

1
2
3
4
5
6
7
8
9
10
public void dynamicHeadWrite() {


EasyExcel.write("学生信息表.xlsx")
.sheet()
.head(setHead())
.doWrite(WriteDemo.getData());


}

结果

可以看到,最后一列内容没有超出单元格了。