使用EasyExcel模版导出

一、官方提供方法

easyexcel官方文档 填充Excel | Easy Excel

官方demo是利用本地模版文件填充并下载到本地

    /**
     * 复杂的填充
     *
     * @since 2.1.1
     */
    @Test
    public void complexFill() {
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        // {} 代表普通变量 {.} 代表是list的变量
        String templateFileName =
            TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx";

        String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx";
        // 方案1
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
            // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
            // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
            // 如果数据量大 list不是最后一行 参照下一个
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            excelWriter.fill(data(), fillConfig, writeSheet);
            excelWriter.fill(data(), fillConfig, writeSheet);
            Map<String, Object> map = MapUtils.newHashMap();
            map.put("date", "2019年10月9日13:28:28");
            map.put("total", 1000);
            excelWriter.fill(map, writeSheet);
        }
    }

二、根据业务场景的方法

我用的是web项目,将输出改为了输出流 OutputStream

	/**
	 * 导出质检任务详情
	 *
	 * @param id 任务主键,用于获取数据
	 */
	@Override
	public void exportTask(Long id, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			// outputStream:要导出的文件的输出流
			OutputStream outputStream = response.getOutputStream();
			// 模版文件
			ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx");
			// 使用模版文件的两种方式:
			// 	1、文件路径:.withTemplate(templateFileName)
			// 	2、输入流:.withTemplate(inputStream)
			String templateFileName = classPathResource.getFile().getPath();
			InputStream inputStream = classPathResource.getInputStream();

			excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build();
			WriteSheet writeSheet = EasyExcel.writerSheet().build();

			// 获取数据 dataList columnList formData
			Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id);

			// 调用微服务获取字典 determine 用来翻译判定结果
			List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData();
			// 调用微服务获取字典 product_unit 用来翻译零件单位
			List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData();

			/* 1、List以外的数据 */
			QmsQcTask formData = (QmsQcTask) mapDetail.get("formData");
			determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel()));
			unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel()));
			excelWriter.fill(formData, writeSheet);
			/* 2、List cols数据 */
			List<Map<String, Object>> cols = new ArrayList<>();
			// 查找抽检数是多少,即有几列
			int num = formData.getSampleNum();
			for (int i = 1; i <= num; i++) {
				Map<String, Object> map = new HashMap<>();
				// 列名
				map.put("label", "" + i);
				cols.add(map);
			}
			// 横向填充
			FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
			excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet);

			/* 3、List details数据 */
			// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
			// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
			// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
			FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
			List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");
			for (int i = 0; i < dataList.size(); i++) {
				Map<String, Object> data = dataList.get(i);
				// 翻译判定合不合格(因为业务和字典不在一个数据库,所以放在代码里处理)
				String determine = (String) data.get("determine");
				determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel()));
				// 如果没有质检标准,每一个质检值都要转为合不合格
				if (StrUtil.isBlank((String) data.get("inspection_standard"))){
					int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0);
					for (int j = 1; j <= samplingNum; j++) {
						String val = (String) data.get(""+j);
						int finalJ = j;
						determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel()));
					}
				}
				// 给数据加上序号
				data.put("index", i + 1);
			}
			excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);

			// 设置输出流格式以及文件名:
			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			response.setCharacterEncoding("utf-8");
			String fileName = URLEncoder.encode("质检任务", "UTF-8").replaceAll("\\+", "%20");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			// 千万别忘记close关闭流
			if (excelWriter != null) {
				excelWriter.close();
			}
		}

	}

 模版:

效果:

三、说明

1、获取输出流

// outputStream:要导出的文件的输出流
OutputStream outputStream = response.getOutputStream();

2、获取模版文件

// 模版文件
ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx");
// 方式一:路径
String templateFileName = classPathResource.getFile().getPath();
// 方式二:输入流
InputStream inputStream = classPathResource.getInputStream();

3、创建 ExcelWriterWriteSheet

excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();

使用模版文件有两种方式,使用其中一种就可以:
    1.文件路径:.withTemplate(templateFileName)
    2.输入流:.withTemplate(inputStream)

4、获取数据

Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id);

我这里直接调用了方法,map包含

dataList主要列表数据 formData 列表外的数据

5、填充数据

1.获取字典数据

// 调用微服务获取字典 determine 用来翻译判定结果
List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData();
// 调用微服务获取字典 product_unit 用来翻译零件单位
List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData();

我的业务数据和字典不在同一个数据库内,sql无法翻译,所以需要在代码里翻译

2.填充列表外的数据

/* 1、List以外的数据 */
			QmsQcTask formData = (QmsQcTask) mapDetail.get("formData");
			determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel()));
			unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel()));
			excelWriter.fill(formData, writeSheet);

只要对象属性名和模版里写的标记已知就可以了

3.填充缺少的列名

模版:

 效果:

 因为我后半部分列名涉及到行转列,且不固定列数,所以需要横着填充上列名。注意列表的名字“cols”要对应起来。

使用.direction(WriteDirectionEnum.HORIZONTAL) 设置为横向填充。

/* 2、List cols数据 */
List<Map<String, Object>> cols = new ArrayList<>();
// 查找抽检数是多少,即有几列
int num = formData.getSampleNum();
for (int i = 1; i <= num; i++) {
	Map<String, Object> map = new HashMap<>();
	// 列名
	map.put("label", "" + i);
	cols.add(map);
}
// 横向填充
FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet);

4.填充主列表数据

/* 3、List details数据 */
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");

/*以下为个人业务处理*/
for (int i = 0; i < dataList.size(); i++) {
	Map<String, Object> data = dataList.get(i);
	// 翻译判定合不合格(因为业务和字典不在一个数据库,所以放在代码里处理)
	String determine = (String) data.get("determine");
	determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel()));
	// 如果没有质检标准,每一个质检值都要转为合不合格
	if (StrUtil.isBlank((String) data.get("inspection_standard"))){
		int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0);
		for (int j = 1; j <= samplingNum; j++) {
			String val = (String) data.get(""+j);
			int finalJ = j;
			determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel()));
		}
	}
	// 给数据加上序号
	data.put("index", i + 1);
}
/*以上为个人业务处理*/

excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);

最重要的三句话:


FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList");


excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存

5.设置输出流格式以及文件名

// 设置输出流格式以及文件名:
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("质检任务", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

四、思想

我的模版使用了动态的列,不确定有几列,所以我就写了一百多个。

还有另外一种设想:

如果先把列表外的数据填充,然后将主列表的列填充完毕,再把主列表的标记写上,这些操作完毕之后把文件输出到另外的输出流上,再将此输出流转为输入流,再把主列表的数据填充,输出到response。

理论上可行,但我没试。

  • 7
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,关于easyexcel模板导出,我可以给你一些基本的介绍和示例代码。 首先,EasyExcel是一个基于JavaExcel操作框架,可以实现对Excel的读写操作,并支持大批量数据的导入导出。其中,模板导出是指在Excel文件中预先设置好样式和格式,然后按照模板的格式将数据填充到对应的单元格中,最终生成一个符合规范的Excel文件。 下面是一个示例代码,演示如何使用EasyExcel进行模板导出: ```java // 创建一个ExcelWriter对象,指定输出文件的路径和模板文件的路径 ExcelWriter writer = new ExcelWriter("output.xlsx", new FileInputStream("template.xlsx")); // 获取输出文件的Sheet对象 Sheet sheet = writer.getSheet(); // 填充数据到指定的单元格中 writer.fillCellData(sheet, 1, 1, "张三"); writer.fillCellData(sheet, 1, 2, 18); writer.fillCellData(sheet, 2, 1, "李四"); writer.fillCellData(sheet, 2, 2, 20); // 写入并关闭输出流 writer.finish(); ``` 在这个示例代码中,我们首先创建一个ExcelWriter对象,指定输出文件的路径和模板文件的路径。然后通过调用writer.getSheet()方法获取输出文件的Sheet对象,并使用writer.fillCellData()方法将数据填充到指定的单元格中。最后,调用writer.finish()方法将数据写入到输出文件并关闭输出流。 需要注意的是,在实际使用过程中,我们需要根据具体的业务需求进行适当的修改和扩展。同时,为了保证代码的可读性和可维护性,建议将具体的操作封装成一个工具类或者服务类,提供给其他模块调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值