easyExcel导出(非注解)
思路:先拿到表头数据,再去封装表数据。
一.动态表头
List<List<String>> headTitles = Lists.newArrayList();//表头数据
说明:外层list表示的表头接口,内层list表示的是表头每一列的数据,而内层list的每一位元素,代表这该行的数据,位序为0的是第一行数据,位序为1的是第二行数据,依次往下。
下面展示一些 代码片段
。
List<List<String>> headTitles = Lists.newArrayList();
headTitles.add(Lists.newArrayList(basicTitle,day,day));//第一列的第一二三行
headTitles.add(Lists.newArrayList(basicTitle,week,week));//第二列的四一二三行
headTitles.add(Lists.newArrayList(basicTitle,dutyPosition1,dutyPosition1));//第三列的一二三行
headTitles.add(Lists.newArrayList(basicTitle,dutyPosition2,dutyPosition2));//第四列的一二三行
headTitles.add(Lists.newArrayList(basicTitle,dutyPosition3,dutyPosition3));//第五列的一二三行
headTitles.add(Lists.newArrayList(basicTitle,dutyPosition4,dutyPosition4));//第六列的一二三行
headTitles.add(Lists.newArrayList(basicTitle,dutyPosition5,dutyPosition5));//第七列的一二三行
注意:这里的表头都是固定的,后面的表头是动态生成的,动态表头的生成方法有很多种,例如可以通过横向填充的方法,不过如果表头比较复杂,还涉及到合并操作,建议使用一下这种方法,不需要合并策略就可以实现复杂表头的导出。
teamList.forEach(item->{
headTitles.add(Lists.newArrayList(basicTitle,empty,item));
});//这里的empty是空串,
表头会自动合并,依据是相邻列的行数据如果值相同会自动合并单元格
最终效果展现:
二.表头数据
表头数据的封装是需要返回一个List<List>,不过不同的是,外层list的数据的位序,代表的是行数,内层list的数据的位序代表的列数,封装的时候,位序不能乱,否则导出的时候,会对不上。这里的代码就不贴了,一个一个塞吧,每一行的数据封装进一个list里面,注意位序,位序为0代表第一列,依次往后
List<List<String>> rowList= new ArrayList<>(fixedTitleSize+DynamicTitle.size())
三.结果封装返回
使用response输出文件流,浏览器下载导出Excel文件
List<List<String>> headTitles = (List<List<String>>)responseMap.get("headTitles");//表头数据,通过service查出来就好了
List<List<String>> rowList = (List<List<String>>)responseMap.get("rowList"); //表数据
String newFilePath = folder + newFileName;
ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(new FileOutputStream(newFilePath));
excelWriterBuilder.head(headTitles);
ExcelWriter excelWriter = excelWriterBuilder.build();
WriteSheet writeSheet = EasyExcel.writerSheet("sheet0").build();
excelWriter.write(rowList,writeSheet);
excelWriter.finish();
下面的代码片等同,会自动关闭输出流
String sheetName = URLEncoder.encode("表名", "UTF-8");
String fileName = sheetName.concat(String.valueOf(System.currentTimeMillis())).concat(".xls");
List<List<String>> headTitles = (List<List<String>>)returnMap.get("headTitles");
//表数据
List<List<String>> rowList = (List<List<String>>)returnMap.get("rowList");
//response输出文件流
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
//写入表头,表数据
EasyExcel.write(response.getOutputStream()).excelType(ExcelTypeEnum.XLS).head(headTitles).sheet("Sheet0").doWrite(rowList);
catch(Excpetion e){
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
response.getWriter().println("导出失败");
}
导出的合并策略,样式还没设置,以后再补全,优化代码去了。
参考文档:easyexcel语雀官方文档
以下补上导出合并策略
合并分向上合并以及左右合并
向上合并引用别的代码如下,加上一些注释吧
public class ExcelFileCellMergeStrategy implements CellWriteHandler {
private int[] mergeColumnIndex;//要合并的列 从0开始索引
private int mergeRowIndex;//合并起始行行 从0开始索引
public ExcelFileCellMergeStrategy(int[] mergeColumnIndex, int mergeRowIndex) {
this.mergeColumnIndex = mergeColumnIndex;
this.mergeRowIndex = mergeRowIndex;
}
public ExcelFileCellMergeStrategy() {
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
//当前行
int curRowIndex = cell.getRowIndex();
//当前列
int curColIndex = cell.getColumnIndex();
if (curColIndex > mergeRowIndex){
for (int columnIndex : mergeColumnIndex) {
if (curColIndex == columnIndex){
mergeWithPrevRow(writeSheetHolder, cell ,curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 当前单元格向上合并
* @param writeSheetHolder
* @param cell
* @param curRowIndex
* @param curColIndex
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
//获取当前行的当前列的数据和上一行的当前列数据,通过上一行数据是否相同进行合并
Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Cell preCell = cell.getSheet().getRow(curRowIndex - 1 ).getCell(curColIndex);
Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
//比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
if (curData.equals(preData)){
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergedRegions.size() && !isMerged; i++) {
CellRangeAddress cellAddresses = mergedRegions.get(i);
//若上 一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellAddresses.isInRange(curRowIndex - 1 , curColIndex)){
sheet.removeMergedRegion(i);
cellAddresses.setLastRow(curRowIndex);
sheet.addMergedRegion(cellAddresses);
isMerged = true;
}
}
//若上一个单元格未被合并,则新增合并单元
if (!isMerged){
CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellAddresses);
}
}
}
}
如果是左右合并的话:
CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, 0, curColIndex);
原理是一样的,只不过就是同行的前后比较,改下代码就可以了,自己debug走一遍就知道怎么改了。
以下是设置公共列宽的策略:
//列宽策略
public class commonColWidthHandler extends AbstractColumnWidthStyleStrategy {
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);
public commonColWidthHandler() {
}
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = (Map)CACHE.get(writeSheetHolder.getSheetNo());
if (maxColumnWidthMap == null) {
maxColumnWidthMap = new HashMap(16);
CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
}
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
if (columnWidth >= 0) {
if (columnWidth > 30) {
columnWidth = 30;
}
Integer maxColumnWidth = (Integer)((Map)maxColumnWidthMap).get(cell.getColumnIndex());
if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
((Map)maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 190);
}
}
}
}
private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData cellData = cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
} else {
switch(type) {
case STRING:
return cellData.getStringValue().getBytes().length;
case BOOLEAN:
return cellData.getBooleanValue().toString().getBytes().length;
case NUMBER:
return cellData.getNumberValue().toString().getBytes().length;
default:
return -1;
}
}
}
}
}
以后还有遗漏我再补充。