通用 Excel 导出功能设计与实现:动态列选择与灵活配置
在企业级应用开发中,数据导出是高频需求。本文介绍一种支持动态列选择、灵活配置的通用 Excel 导出方案,通过前后端协同设计,实现导出字段、列顺序、数据格式的自定义,满足多样化业务场景。
一、功能架构设计
核心特性
- 动态字段选择:支持通过前端勾选动态指定导出字段,包含字段名(逻辑标识)与显示名(业务含义)的映射
- 行数据过滤:支持按用户 ID 筛选导出特定行数据
- 多 Sheet 支持:可扩展支持单个 Excel 文件包含多个 Sheet 页
- 格式自适应:自动处理日期、数字等数据类型的格式化显示
技术栈
- 前端:Thymeleaf 模板引擎 + XMLHttpRequest 文件下载
- 后端:Spring Boot + EasyExcel + Hutool 工具集
- 核心组件:
- 请求参数:ExcelExportRequest(包含基础配置与字段列表)
- 响应结构:ExcelExportResponse(封装文件元信息与 Sheet 数据)
二、核心实现细节
1. 前后端数据协议设计
入参结构(ExcelExportRequest)
@Datapublic class UserExportRequest extends ExcelExportRequest {private List<Integer> userIdList; // 待导出的用户ID列表(可选)}@Datapublic class ExcelExportRequest {private String excelName; // Excel文件名private String sheetName; // Sheet页名称private List<ExcelExportField> fieldList; // 导出字段列表(有序)}@Datapublic class ExcelExportField {private String fieldName; // 实体类字段名(如"userId")private String fieldDesc; // 表格显示名称(如"用户ID")}
出参结构(ExcelExportResponse)
@Datapublic class ExcelExportResponse {private String excelName; // 导出文件名private List<ExcelSheet> sheetList; // Sheet数据集合@Datapublic static class ExcelSheet {private String sheetName; // Sheet名称private List<ExcelHead> headList; // 表头信息private List<Map<String, String>> dataList; // 行数据(键值对形式)@Datapublic static class ExcelHead {private String fieldName; // 字段名private String fieldDesc; // 显示名}}}
2. 前端交互实现
动态列选择组件
<!-- 案例1:仅列选择 --><table border="1"><caption><span class="title">案例1:勾选需要导出的列</span><button onclick="exportExcel1(event)">导出</button></caption><tr><th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th><th><label><input type="checkbox" class="exportCol" data-field-name="userName" data-field-desc="用户名">用户名</label></th><!-- 更多字段... --></tr></table><!-- 案例2:列选择+行筛选 --><table border="1"><caption><span class="title">案例2:勾选需要导出的列 & 行</span><button onclick="exportExcel2(event)">导出</button></caption><tr><th>选择记录</th><th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th><!-- 更多字段... --></tr><tr th:each="user:${userList}"><td><input type="checkbox" class="userId" th:data-user-id="${user.userId}"></td><td th:text="${user.userId}"></td><!-- 数据行展示... --></tr></table>
文件下载逻辑
function download(data, url) {const xhr = new XMLHttpRequest();xhr.open("POST", url);xhr.responseType = 'blob';xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');xhr.onload = function() {if (this.status === 200) {const blob = this.response;if (blob.size > 0) {// 从响应头解析文件名const fileName = getFileNameFromResponse(this.getResponseHeader("content-disposition"));// 创建临时链接下载const a = document.createElement('a');a.href = URL.createObjectURL(blob);a.download = fileName;a.click();}}};xhr.send(JSON.stringify(data));}// 文件名解析工具function getFileNameFromResponse(disposition) {const match = /filename=(.*)/.exec(disposition);return decodeURIComponent(match[1].replace(/['"]/g, ''));}
3. 后端核心处理
控制器设计
@Controller@CrossOriginpublic class UserController {@Resource private UserService userService;// 页面跳转@GetMapping("/userList")public String userList(Model model) {model.addAttribute("userList", userService.getUserList());return "userList";}// 导出接口@PostMapping("/userExport")public void userExport(@RequestBody UserExportRequest request) throws IOException {ExcelExportResponse response = userService.userExport(request);ExcelExportUtils.writeExcelToResponse(response);}}
业务层逻辑
@Servicepublic class UserServiceImpl implements UserService {@Overridepublic ExcelExportResponse userExport(UserExportRequest request) {List<User> dataList;// 处理行筛选逻辑if (CollectionUtil.isEmpty(request.getUserIdList())) {dataList = getUserList(); // 导出全部数据} else {dataList = getUserList(request.getUserIdList()); // 按ID筛选}// 构建导出数据return ExcelExportUtils.build(dataList, request);}// 模拟数据获取private List<User> getUserList() {List<User> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new User(i, "用户名-" + i, 20 + i, "地址-" + i));}return list;}}
导出工具类
public class ExcelExportUtils {public static ExcelExportResponse build(List<?> dataList, ExcelExportRequest request) {ExcelExportResponse result = new ExcelExportResponse();result.setExcelName(request.getExcelName());List<ExcelSheet> sheetList = new ArrayList<>();ExcelSheet sheet = new ExcelSheet();sheet.setSheetName(request.getSheetName());// 构建表头(保持字段顺序)sheet.setHeadList(buildSheetHeadList(request.getFieldList()));// 构建数据行(通过反射获取字段值)sheet.setDataList(buildSheetDataList(dataList, request.getFieldList()));sheetList.add(sheet);result.setSheetList(sheetList);return result;}private static List<ExcelSheet.ExcelHead> buildSheetHeadList(List<ExcelExportField> fields) {return fields.stream().map(field -> new ExcelSheet.ExcelHead(field.getFieldName(), field.getFieldDesc())).collect(Collectors.toList());}// 反射获取对象字段值private static List<Map<String, String>> buildSheetDataList(List<?> dataList, List<ExcelExportField> fields) {return dataList.stream().map(obj -> {Map<String, String> row = new HashMap<>();fields.forEach(field -> {Object value = ReflectUtil.getFieldValue(obj, field.getFieldName());row.put(field.getFieldName(), Objects.toString(value, ""));});return row;}).collect(Collectors.toList());}// 响应输出处理public static void writeExcelToResponse(ExcelExportResponse result) throws IOException {HttpServletResponse response = getResponse();response.setContentType("application/vnd.ms-excel");response.setHeader("Content-Disposition","attachment; filename=" + URLEncodeUtil.encode(result.getExcelName() + ".xlsx"));try (ExcelWriter writer = EasyExcel.write(response.getOutputStream()).build()) {result.getSheetList().forEach(sheet -> {WriteSheet writeSheet = EasyExcel.writerSheet(sheet.getSheetName()).build();// 写入表头与数据writer.write(buildEasyExcelData(sheet), writeSheet);});}}}
三、方案优势分析
- 灵活性:通过fieldList实现导出字段的动态排序与筛选,适应不同业务视图需求
- 扩展性:支持添加多 Sheet、数据格式化(如日期转换)、样式配置等扩展功能
- 易用性:前端可视化勾选操作,后端自动处理反射映射,降低使用门槛
- 性能优化:通过工具类封装重复逻辑,减少代码冗余,提升开发效率
四、应用场景
- 数据报表导出:支持不同角色用户自定义报表字段
- 批量数据下载:结合行筛选功能实现精准数据提取
- 系统对接:为第三方系统提供标准化 Excel 数据输出接口
通过该方案,开发者可快速实现具备灵活配置能力的 Excel 导出功能,同时保持代码的可维护性与扩展性。实际应用中可根据业务需求,进一步扩展数据格式化、单元格样式、多语言支持等高级功能。