1有俩种方法进行表的导入和导出,俩种各有千秋
一、Java POI 由于功能丰富,POI的学习曲线相对较陡峭,对于新手来说可能不太友好。且填充时是按照单元格一个一个填充,设置表头时不友好,填充数据时可以自定义较为简单
二、EasyExcel 对于数据量较大的填充。EasyExcel提供了更优的性能,因为它采用了基于SAX的读写方式,减少了内存的占用和垃圾回收的压力。如复杂的数据结构转换、自定义公式等,EasyExcel可能不如POI灵活。 由于是动态的表头,所以说本文采用EasyExcel进行动态表头的转化比较简单,但对于数据的灵活性没有POI好,
ps:总结,POI设置表头复杂,数据填充简单,EasyExcel表头简单,数据填充较为复杂,且easyExcel对新手稍微友好一些,刚实习,使用easyexcel。
2EasyExcel 官网文档设置动态列表还是较为简单的,参考easy官方文档,设置表头,
1 设置固定的表头,一般情况下会有些固定的表头,匹配dto中的一些固定的字段,例如,部门,系统,和一些动态的表头,如何生成?
2导出的表头如图,设置表头时,需要进行的就是创建一个List<List<String>> 创建时,需要在上面进行,
List<List<String>> headList = Lists.newArrayList();
List<String> head0 = Lists.newArrayList();
head0.add("体系");
List<String> head1 = Lists.newArrayList();
head1.add("系统");
List<String> head2 = Lists.newArrayList();
head2.add("标工编码");
List<String> head5 = Lists.newArrayList();
head3.add("工序名称");
// 根据 接口返回值生产表头,
modelList.forEach(x->{
x.getColumnList().forEach(y->{
List<String> head = Lists.newArrayList();
//设置动态表头的内容,easyExcel会根据其内容进行生成
head.add(x.getMclassifyName());
head.add(y.getModelName());
head.add(y.getModelCode());
headList.add(head);
});
});你撤回了一条消息重新编辑
3设置数据集,采用最原始的方法进行,根据表头对应的特定值,对其进行遍历,生成一个List<OrderData> list中每个值就是其中的一行,会根据表头进行寻找,我的比较复杂原因是中间有一个我的dto里面有一个list,需要对list进行转列。主要这个toMap,转化成map类型
ps:第4步发我需要转换的dto字段
private List<List<Object>> setPrimariyDataList(List<Dto> Dtos,List<List<String>> headList) {
List<Map<String, Object>> result = null;
//将需要导出的字段转换成map
result = Dtos.stream().map(Dto::toMap).collect(Collectors.toList());
//根据表头进行填充,对应的是表头上面的字段
List<List<Object>> orderedData = new ArrayList<>();
for (Map<String, Object> map : result) {
List<Object> rowData = new ArrayList<>();
for (List<String> header : headList) {
for (String key : header) {
if (map.containsKey(key)) {
Object value = map.get(key);
if (value ==null) {
rowData.add(" ");
}else {
if (map.get(key).equals(true)){
rowData.add("\u2713");
}else if (map.get(key).equals(false)){
rowData.add(" ");
}else {
rowData.add(map.get(key));
}
}
}
}
}
orderedData.add(rowData);
}
return orderedData;
}
4对于我需要导出的dto进行解析 搞清楚他的数据结构,才能知道你需要的数据在哪里
public Class Dto{
private String setupName;
private String productCode;
private String processCode;
private String processName;
// modelLibraryList 可以根据接口去动态的设置,导致表头是动态的
privat List<modelDtos> modelLibraryList;
/**
* 研发固定表头
* @return
*/
public Map<String, Object> toMap(){
Map<String, Object> map = Maps.newHashMap();
map.put("体系", this.setupName);
map.put("系统", this.productCode);
map.put("标工编码",this.processCode);
map.put("工序名称",this.processName);
List<Map<String, Object>> collectedMaps = modelLibraryList.stream()
.map(dto -> {
Map<String, Object> map1 = new HashMap<>();
map1.put(dto.getModelName(), dto.getSelectFlag());
return map1;
})
.collect(Collectors.toList());
collectedMaps.forEach(item ->{
map.putAll(item);
});
return map;
}
}
5 对于数据进行导出时,直接将做好的list<list<String>>headlist和list<list<String>>dataList 用controller层导出即可
//设置响应内容类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");//编码
// 设置文件名, ps:把字符串中所有的'+'替换成'%20',在URL中%20代表空格
String fileName = URLEncoder.encode("标模库导出", "UTF-8").replaceAll("\\+", "%20");
//设置响应头
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
response.setHeader("fileName", fileName + ".xlsx");//设置响应头
EasyExcel.write(response.getOutputStream()).sheet("标模数据").head(headList).doWrite(dataList);
3需要设置一些样式,对特定的行和列设置样式,比如一些符合某些条件的dto ,鼠鼠的感同身受是,
类似于这种,可以使用一个List<TargetCell> 记录下当前的行号和 表头中需要修改的单元格,在后续中 自定义一个MyCellWriterHandler ,实现CellWriterHandler 即可,重写其中的,afterCellDispose ,先判断是否为表头,如果不是表头就可以继续了
public void afterCellDispose(CellWriteHandlerContext context) {
if (BooleanUtils.isNotTrue(context.getHead())) {
Cell cell = context.getCell();
int curRowIndex = cell.getRowIndex();
int curColIndex = cell.getColumnIndex();
Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
// 根据目标单元格列表进行匹配并设置样式
for (TargetCell targetCell : targetCells) {
if (targetCell.getRowIndex() == curRowIndex &&
getColumnName(context).equals(targetCell.getColumnName())) {
CellStyle cellStyle = workbook.createCellStyle();
// 根据目标单元格配置相应颜色
byte[] rgb = decideRgbColor(targetCell);
XSSFCellStyle xssfCellColorStyle = (XSSFCellStyle) cellStyle;
xssfCellColorStyle.setFillForegroundColor(new XSSFColor(rgb, null));
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(cellStyle);
context.getFirstCellData().setWriteCellStyle(null);
break; // 找到匹配的目标单元格后,无需再遍历
}
}
}
}
当然对于上面的targetCell的条件判断,这里有不准的地方,后面自定义调整,如果你想在自定义的Handler中传参数,则,可在其中定义构造方法,这样可以获得你需要的参数,。
public class StyleHandler implements CellWriteHandler {
// 目标单元格信息列表
private final List<TargetCell> targetCells;
private final List<List<String>> headList;
public StyleHandler(List<TargetCell> targetCells ,List<List<String>> headList) {
this.targetCells = targetCells;
this.headList = headList;
}
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
//在填充数据之后,对于特定的地方进行样式填充
if(条件 和当前行和列有关){
cell.setCellStyle(new CellStyle)
}
// 还可以设置一些单元格和并信息
if(条件 和当前行和列有关){
// 创建区域对象并设置合并区域
CellRangeAddress range = new CellRangeAddress(startRowIndex, endRowIndex, colIndex, colIndex);
sheet.addMergedRegion(range);
}
}