文章目录
- 1、EasyExce依赖准备
- 2、通过注解Bean的方式生成Excel
- 2.1、注解Bean准备
- 2.2、封装数据,生成Excel(只需要几行代码)
- 2.3、生成结果展示
- 3、通过Excel模板生成数据
- 3.1、准备编写Excel模板
- 3.2、封装数据,生成excel
- 3.3、模板导出数据结果展示
- 附录
- 占位符为什么是`{.字段名}`?源码分析
- 参考链接
1、EasyExce依赖准备
- EasyExcel依赖
<!-- easyexcel: excel快速导出--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.0</version></dependency>
- apache poi依赖
EasyExcel同时依赖Apache POI库,同时还需要注意下两个库的版本对应关系。EasyExcel可能升级对poi的依赖,导致功能不兼容。
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></dependency>
2、通过注解Bean的方式生成Excel
2.1、注解Bean准备
EasyExcel注解部分参考:EasyExcel注解大全_起一个让人印象深刻的名字的博客-CSDN博客
package com.dongzi.utils.excel.bean;import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.*;
import com.alibaba.excel.enums.BooleanEnum;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
import lombok.Data;
import lombok.experimental.Accessors;import java.util.Date;@Data // lombok提供getter\setter等
@Accessors(chain = true) // lombok提供链式操作
@ContentRowHeight(25) // 行高
public class FillData {@HeadStyle(fillBackgroundColor = 4) // 表头样式设置,背景颜色填充,颜色类型4@HeadFontStyle(fontName = "宋体", fontHeightInPoints = 25, color = 3) // 表头字体设置,宋体,颜色3@ExcelProperty(value = "姓名") // 列名@ColumnWidth(20) // 列宽@ContentFontStyle(fontName = "宋体", color = 4, bold = BooleanEnum.TRUE) // 数据部分单元格字体样式@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)// 数据部分单元格样式private String name; // 字段@HeadStyle(fillBackgroundColor = 4)@HeadFontStyle(fontName = "楷体", fontHeightInPoints = 25, color = 3)@ExcelProperty(value = "地址")@ColumnWidth(20)@ContentFontStyle(fontName = "楷体")@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)private String address;@HeadStyle(fillBackgroundColor = 4)@HeadFontStyle(fontName = "微软雅黑", fontHeightInPoints = 25, color = 3)@ExcelProperty(value = "时间")@ColumnWidth(50)@ContentFontStyle(fontName = "微软雅黑")@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)@DateTimeFormat(value = "yyyy-MM-dd") // 时间格式化private Date date;// 忽略生成的字段@ExcelIgnoreprivate String welcome;}
2.2、封装数据,生成Excel(只需要几行代码)
package com.dongzi.utils.excel;import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.dongzi.utils.excel.bean.FillData;
import org.springframework.core.io.ClassPathResource;import java.io.File;
import java.io.IOException;
import java.util.*;public class ReadeExcelTemplate {public static void main(String[] args) throws IOException {exportDataByAnnotation();}// 基于注解导出数据public static void exportDataByAnnotation() {// 文件输出位置File outPutFile = new File("D:/temp/Excel-基于注解导出数据.xlsx");// 指定使用哪个注解Bean来生成Excel?ExcelWriter excelWriter = EasyExcel.write(outPutFile, FillData.class).excelType(ExcelTypeEnum.XLSX).build();// 生成数据excelWriter.write(fillData(5), EasyExcelFactory.writerSheet(0).build());// 完成,写数据到本地excelWriter.finish();}public static List<FillData> fillData(int num) {List<FillData> mapList = new ArrayList<>();for (int i = 0; i < num; i++) {FillData fillData = new FillData();fillData.setName("姓名" + i).setAddress("住址" + i).setWelcome("你好" + i).setDate(new Date());mapList.add(fillData);}return mapList;}
}
2.3、生成结果展示
3、通过Excel模板生成数据
3.1、准备编写Excel模板
注意!
- 占位符的书写格式为
{.字段名}
(为什么占位符是这样的,详见附录) - 一个单元格个可以写多个占位符
- 模板需要按照预定的格式/样式调整好,之后会按照相同格式输出单元格
- 数据输出视占位符所在位置依次向下输出
如图:Sheet1模板
如图:Sheet2模板
如图:Sheet3模板
3.2、封装数据,生成excel
package com.dongzi.utils.excel;import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.dongzi.utils.excel.bean.FillData;
import org.springframework.core.io.ClassPathResource;import java.io.File;
import java.io.IOException;
import java.util.*;public class ReadeExcelTemplate {public static void main(String[] args) throws IOException {exportDataByTemplate();}// 基于excel模板导出数据public static void exportDataByTemplate() throws IOException {File outPutFile = new File("D:/temp/Excel-基于模板导出数据.xlsx");ClassPathResource pathResource = new ClassPathResource("template/info.xlsx");// 基于excel模板导出数据ExcelWriter excelWriter = EasyExcel.write(outPutFile).withTemplate(pathResource.getInputStream()).excelType(ExcelTypeEnum.XLSX).build();// excelWriter.write(fillData(5), EasyExcel.writerSheet(0).build());
// excelWriter.write(fillData(10), EasyExcel.writerSheet(1).build());
// excelWriter.write(fillData(15), EasyExcel.writerSheet(2).build());// {.name}-{.welcome}, {.address}, {.date} 占位符位置数据补充,一个单元格可以写多个{.字段名}// 数据的位置从当前sheet页的的占位符位置开始,无论title在什么位置// 注意这里必须使用excelWriter.fill()进行数据填充excelWriter.fill(fillData(5), EasyExcel.writerSheet(0).build());excelWriter.fill(fillData(10), EasyExcel.writerSheet(1).build());excelWriter.fill(fillData(15), EasyExcelFactory.writerSheet(2).build());// {.name}, {.address}, {.date} 占位符位置数据补充
// excelWriter.fill(fillData2(5), EasyExcel.writerSheet(0).build());
// excelWriter.fill(fillData2(10), EasyExcel.writerSheet(1).build());
// excelWriter.fill(fillData2(15), EasyExcelFactory.writerSheet(2).build());excelWriter.finish();}public static List<Map<String, Object>> fillData2(int num) {List<Map<String, Object>> mapList = new ArrayList<>();for (int i = 0; i < num; i++) {Map<String, Object> beanMap = new HashMap<>();beanMap.put("name", "姓名" + i);beanMap.put("welcome", "你好" + i);beanMap.put("address", "地址" + i);beanMap.put("date", DateUtil.format(new Date(), "yyyy-MM-dd"));mapList.add(beanMap);}return mapList;}
}
3.3、模板导出数据结果展示
如图:Sheet1,中间框,加边框
如图:Sheet2,第三列加边框
如图:Sheet3,数据随着占位符的位置向下生成
附录
占位符为什么是{.字段名}
?源码分析
从excelWriter.fill()
逐行调试,发现:在读excel模板的时候,会先进行占位符的解析。看源码:
包路径: package com.alibaba.excel.write.executor;
private List<AnalysisCell> readTemplateData(Map<ExcelWriteFillExecutor.UniqueDataFlagKey, List<AnalysisCell>> analysisCache) {List<AnalysisCell> analysisCellList = (List)analysisCache.get(this.currentUniqueDataFlag);if (analysisCellList != null) {return analysisCellList;} else {Sheet sheet = this.writeContext.writeSheetHolder().getCachedSheet();Map<ExcelWriteFillExecutor.UniqueDataFlagKey, Set<Integer>> firstRowCache = MapUtils.newHashMapWithExpectedSize(8);for(int i = 0; i <= sheet.getLastRowNum(); ++i) {Row row = sheet.getRow(i);if (row != null) {for(int j = 0; j < row.getLastCellNum(); ++j) {Cell cell = row.getCell(j);if (cell != null) {// 解析单元格占位符String preparedData = this.prepareData(cell, i, j, firstRowCache);if (preparedData != null) {cell.setCellValue(preparedData);}}}}}return (List)analysisCache.get(this.currentUniqueDataFlag);}}private String prepareData(Cell cell, int rowIndex, int columnIndex, Map<ExcelWriteFillExecutor.UniqueDataFlagKey, Set<Integer>> firstRowCache) {if (!CellType.STRING.equals(cell.getCellType())) {return null;} else {String value = cell.getStringCellValue();if (StringUtils.isEmpty(value)) {return null;} else {StringBuilder preparedData = new StringBuilder();AnalysisCell analysisCell = null;int startIndex = 0;int length = value.length();int lastPrepareDataIndex = 0;label81:while(startIndex < length) {// 对单元格占位符进行分割,取出字段名,之后按字段名去取值// 有兴趣的可以逐行调试int prefixIndex = value.indexOf("{", startIndex);if (prefixIndex < 0) {break;}if (prefixIndex != 0) {char prefixPrefixChar = value.charAt(prefixIndex - 1);if (prefixPrefixChar == '\\') {startIndex = prefixIndex + 1;continue;}}int suffixIndex = -1;while(suffixIndex == -1 && startIndex < length) {suffixIndex = value.indexOf("}", startIndex + 1);if (suffixIndex < 0) {break label81;}startIndex = suffixIndex + 1;char prefixSuffixChar = value.charAt(suffixIndex - 1);if (prefixSuffixChar == '\\') {suffixIndex = -1;}}if (analysisCell == null) {analysisCell = this.initAnalysisCell(rowIndex, columnIndex);}String variable = value.substring(prefixIndex + 1, suffixIndex);if (!StringUtils.isEmpty(variable)) {int collectPrefixIndex = variable.indexOf(".");if (collectPrefixIndex > -1) {if (collectPrefixIndex != 0) {analysisCell.setPrefix(variable.substring(0, collectPrefixIndex));}variable = variable.substring(collectPrefixIndex + 1);if (StringUtils.isEmpty(variable)) {continue;}analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION);}analysisCell.getVariableList().add(variable);if (lastPrepareDataIndex == prefixIndex) {analysisCell.getPrepareDataList().add("");if (lastPrepareDataIndex != 0) {analysisCell.setOnlyOneVariable(Boolean.FALSE);}} else {String data = this.convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex));preparedData.append(data);analysisCell.getPrepareDataList().add(data);analysisCell.setOnlyOneVariable(Boolean.FALSE);}lastPrepareDataIndex = suffixIndex + 1;}}if (analysisCell != null && CollectionUtils.isNotEmpty(analysisCell.getVariableList())) {cell.setBlank();}return this.dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, firstRowCache, preparedData);}}}
参考链接
EasyExcel读取模板填充数据_easyexcel填充模板_hello_cmy的博客-CSDN博客