导出功能相关-自定义表头,列宽 easyExcel

	@GetMapping("/getZipAirSheetYearCustReportExportAll")
	@ApiOperationSupport(order = 1)
	public void getZipAirSheetYearCustReportExportAll(ZipAirSheetVO zipAirSheetVO, HttpServletResponse response) throws IOException {

		try {
			// 1. 获取数据
			List<Map<String, Object>> zipAirSheetYearCustReport = zipAirSaleSheetService.getZipAirSheetYearCustReport(zipAirSheetVO);

			// 确保列表不为空
			if (zipAirSheetYearCustReport == null || zipAirSheetYearCustReport.isEmpty()) {
				throw new RuntimeException("数据列表为空");
			}

			// 获取第一行数据
			Map<String, Object> firstRow = zipAirSheetYearCustReport.get(0);

			// 获取 allWeek 的 Object 值
			Object allWeekValue = firstRow.get("allWeek");

			// 检查空值
			if (allWeekValue == null) {
				throw new RuntimeException("allWeek 字段值为空");
			}

			// 检查类型是否为 List
			if (!(allWeekValue instanceof List)) {
				throw new ClassCastException("allWeek 字段不是 List 类型");
			}

			// 转换为原始 List
			List<?> rawList = (List<?>) allWeekValue;

			// 转换为特定泛型类型(例如 String)
			List<String> allWeekList = new ArrayList<>();
			for (Object item : rawList) {
				if (item instanceof String) {
					allWeekList.add((String) item);
				} else {
					// 处理类型不匹配的元素
					LOGGER.warn("忽略非字符串元素: {}", item);
				}
			}

			// 使用 allWeekList 进行后续操作
			System.out.println("转换后的列表: " + allWeekList);


			// 2. 定义动态表头映射(确保顺序使用 LinkedHashMap)
			Map<String, String> headerMapping = new LinkedHashMap<>();
			headerMapping.put("userName", "用户名称");
			allWeekList.forEach(item -> {
				headerMapping.put(item, item);
			});
			headerMapping.put("total", "合计");
			// 3. 动态生成表头和数据(适配 EasyExcel)
			List<List<String>> head = new ArrayList<>();
			for (Map.Entry<String, String> entry : headerMapping.entrySet()) {
				List<String> headColumn = new ArrayList<>();
				headColumn.add(entry.getValue()); // 表头名称
				head.add(headColumn);
			}


			// 执行转换
			List<Map<String, Object>> transformedList = DataStructureConverter.convertDataStructure(zipAirSheetYearCustReport);


			List<List<Object>> dataList = new ArrayList<>();
			for (Map<String, Object> rowData : transformedList) {
				List<Object> row = new ArrayList<>();
				for (Map.Entry<String, String> entry : headerMapping.entrySet()) {
					Object value = rowData.get(entry.getKey());
					row.add(value != null ? value : ""); // 处理空值
				}
				dataList.add(row);
			}

			// 4. 设置响应头(文件名处理)
			String unsafeFileName = "压缩空气自定义报表" + DateUtils.getCurrentDateStrByFormat(DateFormat.Y_M_D_HMS) + ".xlsx";
			String safeFileName = unsafeFileName.replaceAll("[^a-zA-Z0-9.-]", "_");
			String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString());
			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName + "; filename*=utf-8''" + encodedFileName);


			// 2. 自定义列宽策略
			AbstractColumnWidthStyleStrategy columnWidthStyleStrategy = new AbstractColumnWidthStyleStrategy() {
				@Override
				protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
					if (isHead) {
						// 设置表头列宽
						writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 30 * 256); // 20个字符宽度
					} else {
						// 设置数据列宽
						writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 20 * 256); // 15个字符宽度
					}
				}
			};

			// 5. 使用 EasyExcel 直接写入 HTTP 响应流
			EasyExcel.write(response.getOutputStream())
				.head(head) // 动态表头
				.registerWriteHandler(columnWidthStyleStrategy) // 设置表头样式
				.sheet("压缩空气自定义报表") // Sheet 名称
				.doWrite(dataList); // 动态数据
		} catch (IOException e) {
			LOGGER.error("导出数据异常", e);
			throw new RuntimeException("导出文件失败", e);
		} catch (Exception e) {
			LOGGER.error("系统异常", e);
			throw new RuntimeException(e);
		}
	}

DataStructureConverter 转换类(因为数据返回的是Map,然后转换成普通的类的返回格式,动态类)
package org.springblade.common.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class DataStructureConverter {

	public static List<Map<String, Object>> convertDataStructure(List<Map<String, Object>> originalList) {
		List<Map<String, Object>> transformedList = new ArrayList<>();

		for (Map<String, Object> originalMap : originalList) {
			// 创建新的 Map,使用 LinkedHashMap 保持字段顺序
			Map<String, Object> newMap = new LinkedHashMap<>();

			// 1. 复制原始 Map 中除了 "map" 的字段
			for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
				String key = entry.getKey();
				if (!"map".equals(key)) {
					newMap.put(key, entry.getValue());
				}
			}

			// 2. 提取并合并 "map" 中的键值对到新 Map
			Object mapValue = originalMap.get("map");
			if (mapValue instanceof Map) {
				Map<?, ?> innerMap = (Map<?, ?>) mapValue;
				for (Map.Entry<?, ?> innerEntry : innerMap.entrySet()) {
					String innerKey = String.valueOf(innerEntry.getKey());
					Object innerValue = innerEntry.getValue();
					newMap.put(innerKey, innerValue);
				}
			} else {
				// 可选:处理 "map" 字段不存在或类型错误的情况
				// throw new RuntimeException("map 字段类型错误或不存在");
			}

			// 3. 将转换后的 Map 添加到结果列表
			transformedList.add(newMap);
		}

		return transformedList;
	}
}

### 易于理解的 EasyExcel 自定义方法 在 EasyExcel 中,可以通过多种方式实现自定义设置。以下是几种常见的方法及其具体实现: #### 方法一:通过 `registerWriteHandler` 注册自定义处理器 可以创建一个继承 `AbstractCellWriteHandler` 的类来处理单元格写入逻辑,并重写其方法以调整。 ```java import com.alibaba.excel.write.handler.CellWriteHandler; import org.apache.poi.ss.usermodel.*; import java.util.List; public class CustomColumnWidthHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheet writeSheet, WriteTable writeTable, Row row, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} @Override public void afterCellCreated(WriteSheet writeSheet, WriteTable writeTable, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {} @Override public void afterRowDispose(WriteSheet writeSheet, WriteTable writeTable, Row row, Integer relativeRowIndex, Boolean isHead) { Sheet sheet = row.getSheet(); List<org.apache.poi.ss.util.ColumnRangeAddress> columnRanges = sheet.getMergedRegions(); // 设置特定度 int genderColumnIndex = 0; // 假设性别为第1 int addressColumnIndex = 2; // 假设地址为第3 sheet.setColumnWidth(genderColumnIndex, 10 * 256); // 性别度为10字符[^1] sheet.setColumnWidth(addressColumnIndex, 50 * 256); // 地址度为50字符 } } ``` 随后,在导出 Excel 文件时,需调用 `registerWriteHandler` 将该处理器注册至 EasyExcel 配置中。 ```java import com.alibaba.excel.EasyExcel; import java.util.ArrayList; import java.util.List; public static void main(String[] args) { String fileName = "custom_width.xlsx"; List<UserData> data = new ArrayList<>(); data.add(new UserData("张三", "男", "北京市")); data.add(new UserData("李四", "女", "上海市")); EasyExcel.write(fileName, UserData.class) .registerWriteHandler(new CustomColumnWidthHandler()) // 注册自定义处理器 .sheet("用户数据").doWrite(data); } class UserData { private String name; private String gender; private String address; public UserData() {} public UserData(String name, String gender, String address) { this.name = name; this.gender = gender; this.address = address; } // Getter 和 Setter 方法省略 } ``` --- #### 方法二:基于实体类注解的方式 如果希望直接利用注解控制,则可以在实体类字段上添加 `@ExcelProperty` 并配合其他扩展属性完成配置。 ```java import com.alibaba.excel.annotation.ExcelProperty; public class UserEntity { @ExcelProperty(value = "姓名", width = 20) // 设置名为“姓名”,度为20字符[^2] private String name; @ExcelProperty(value = "性别", width = 10) // 设置名为“性别”,度为10字符 private String gender; @ExcelProperty(value = "地址", width = 50) // 设置名为“地址”,度为50字符 private String address; // Getter 和 Setter 方法省略 } ``` 此方法适用于静态场景下的设定,但对于动态变化的需求可能不够灵活。 --- #### 方法三:完全动态化管理 当面对复杂的业务需求(如表头项不确定或随内容而变),可采用无注解模式并结合监听器机制动态计算每一的最佳度。 ```java import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; // 动态分析每最大长度以便后续适配最佳度 public class DynamicWidthListener extends AnalysisEventListener<Object> { private final Map<Integer, Integer> maxColumnLengths = new HashMap<>(); @Override public void invoke(Object o, AnalysisContext context) { if (o instanceof RowData) { // 假定导入的数据封装成 RowData 对象形式 RowData rowData = (RowData)o; for(int i=0;i<rowData.size();i++) { String value = rowData.getCell(i).toString(); int currentLen = value.length(); maxColumnLengths.putIfAbsent(i, 0); maxColumnLengths.computeIfPresent(i, (key,val)-> Math.max(val,currentLen)); } } } @Override public void doAfterAllAnalysed(AnalysisContext context) { System.out.println("各的最大长度:" + maxColumnLengths.toString()); } } ``` 最终依据上述统计结果重新分配实际使用的即可满足自动化需求[^4]。 --- #### 注意事项 - 使用 `setColumnWidth()` 函数指定单位是以字符数乘以固定比例值的形式表示。 - 如果涉及多工作簿或多表格操作,请确保针对不同区域分别应用对应的样式规则[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值