EasyExcel 템플릿을 사용하여 내보내기

1. 공식적인 제공 방법

easyexcel 공식 문서 채우기 Excel | Easy Excel

공식 데모는 로컬 템플릿 파일로 채워져 로컬로 다운로드됩니다.

    /**
     * 复杂的填充
     *
     * @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);
        }
    }

2. 비즈니스 시나리오에 따른 방법

웹 프로젝트를 사용 중이며 출력을 출력 스트림 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();
			}
		}

	}

 원판:

효과:

 

3. 설명

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);

여기에서 메소드를 직접 호출했고 지도에 다음이 포함되어 있습니다.

dataList 기본 목록 데이터 형식목록 외부의 데이터 데이터

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를 사용합니다. 즉, 목록에 쓸 때 목록 아래에 빈 행이 있는지 여부에 관계없이 행이 생성되고 다음 데이터가 뒤로 이동합니다. 기본값은 false이며 다음 줄이 직접 사용되며 아무도 없으면 생성됩니다. 
forceNewRow를 true로 설정하면 모든 데이터를 메모리에 집어넣는다는 단점이 있으니 주의해서 사용하세요 간단하게 말해서 
템플릿에 리스트가 있고 그 리스트가 마지막 행이 아니라면 꼭 설정해줘야 합니다 아래에 아직 채워야 할 데이터가 있는 경우 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");

4. 또 다른 생각

내 템플릿은 동적 열을 사용하며 얼마나 많은 열이 있는지 잘 모르기 때문에 100개 이상을 작성했습니다.

 

또 다른 아이디어가 있습니다.

목록 외부의 데이터를 먼저 채우면 메인 목록의 열을 채운 다음 메인 목록의 표시를 작성하고 이러한 작업이 완료된 후 파일을 다른 출력 스트림으로 출력한 다음 출력합니다. stream은 입력 스트림으로 변환된 후 메인 리스트에 데이터를 채워 응답으로 출력한다.

이론적으로는 가능하지만 시도하지는 않았습니다.

추천

출처blog.csdn.net/weixin_45735511/article/details/127762821