Exportar utilizando la plantilla EasyExcel

1. Método oficial de prestación

easyexcel documento oficial llenar Excel | Easy Excel

La demostración oficial está llena de archivos de plantilla locales y se descarga en el local.

    /**
     * 复杂的填充
     *
     * @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. Método según escenarios de negocio

Estoy usando un proyecto web y cambié la salida a la secuencia de salida 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();
			}
		}

	}

 plantilla:

Efecto:

 

3. Descripción

1. Obtener el flujo de salida

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

2. Obtenga el archivo de plantilla

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

3. Crear ExcelWriter y WriteSheet

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

Hay dos formas de usar archivos de plantilla, use una de ellas:
    1. Ruta del archivo: .withTemplate(templateFileName)
    2. Flujo de entrada: .withTemplate(inputStream)

4. Obtener datos

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

Llamé al método directamente aquí, y el mapa contiene

dataList formulario de datos de la lista principalDatos de datos fuera de la lista

5. Datos de relleno

1. Obtener datos del diccionario

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

Mis datos comerciales y el diccionario no están en la misma base de datos, sql no se puede traducir, por lo que debe traducirse en el código

2. Completar los datos fuera de la lista

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

Siempre que se conozcan el nombre del atributo del objeto y la etiqueta escrita en la plantilla

3. Nombres de columna de relleno con pocas columnas

plantilla:

 Efecto:

 Debido a que la segunda mitad de los nombres de mis columnas involucran la conversión de fila a columna, y la cantidad de columnas no es fija, los nombres de las columnas superiores deben completarse horizontalmente. Tenga en cuenta que el nombre de la lista "cols" debe corresponder.

Utilice .direction(WriteDirectionEnum.HORIZONTAL) para configurar el relleno 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. Complete los datos de la lista principal

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

Las tres frases más importantes:


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);
Tenga en cuenta aquí que el parámetro de entrada usa forceNewRow, lo que significa que al escribir en la lista, se creará una fila independientemente de si hay una fila en blanco debajo de la lista, y luego los siguientes datos se moverán hacia atrás. El valor predeterminado es falso, la siguiente línea se usará directamente, si no hay nadie, se creará. 
Si forceNewRow se establece en verdadero, existe la desventaja de que colocará todos los datos en la memoria, así que utilícelo con precaución. En pocas palabras, 
si su plantilla tiene una lista y la lista no es la última fila, debe configurarla. si todavía hay datos a continuación para ser llenados forceNewRow=true Pero esto pondrá todos los datos en la memoria y consumirá mucha memoria

5. Establezca el formato de flujo de salida y el nombre del archivo

// 设置输出流格式以及文件名:
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. Otro pensamiento

Mi plantilla usa columnas dinámicas, no estoy seguro de cuántas columnas hay, así que escribí más de cien.

 

Hay otra idea:

Si los datos fuera de la lista se completan primero, luego se completan las columnas de la lista principal y luego se escribe la marca de la lista principal.Después de completar estas operaciones, el archivo se envía a otra secuencia de salida y luego la salida El flujo se convierte en un flujo de entrada. Luego, complete los datos en la lista principal y envíelos a la respuesta.

Es teóricamente posible, pero no lo he probado.

Supongo que te gusta

Origin blog.csdn.net/weixin_45735511/article/details/127762821
Recomendado
Clasificación