springboot项目实现导出excel动态设置表头

在这里插入图片描述

pom.xml

<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.9</version>
		</dependency>

ExcelCustomExportUtil.java

package com.jiuzhu.server.common.utils;

import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.apache.poi.ss.usermodel.CellStyle.ALIGN_CENTER;
import static org.apache.poi.ss.usermodel.CellStyle.VERTICAL_CENTER;

public class ExcelCustomExportUtil {
    public static void exportExcel(HttpServletResponse response, List<String> headers, List<List<Object>> data) throws IOException {
        // 创建工作簿
        Workbook workbook = new XSSFWorkbook();
        // 创建工作表
        Sheet sheet = workbook.createSheet("sheet1");
        Map<String, CellStyle> styles = createStyles(workbook);

        CellStyle style = sheet.getWorkbook().createCellStyle();
        // 设置字体为黑体
        Font font = sheet.getWorkbook().createFont();
        font.setFontName("黑体"); // 或者使用 "SimHei" 表示黑体
        font.setBoldweight((short) 1);      // 设置为粗体(黑体通常是粗体)
        style.setFont(font);
        style.setAlignment(ALIGN_CENTER);
        style.setVerticalAlignment(VERTICAL_CENTER);
        font.setColor(IndexedColors.WHITE.getIndex()); // 设置字体颜色为白色

        // 创建表头
        Row headerRow = sheet.createRow(0);
//        headerRow.setHeightInPoints(30);
        for (int i = 0; i < headers.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellStyle(styles.get("header"));
            cell.setCellValue(headers.get(i));
        }

        // 填充数据
        int rowIndex = 1;
        for (List<Object> rowData : data) {
            Row row = sheet.createRow(rowIndex++);
            for (int i = 0; i < rowData.size(); i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(rowData.get(i).toString());
            }
        }

        // 设置响应头
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");

        // 将工作簿写入输出流
        try (OutputStream outputStream = response.getOutputStream()) {
            workbook.write(outputStream);
        }
        // 关闭工作簿
//        workbook.close();
//        workbook.cloneSheet(0);
    }
    /**
     * 创建表格样式
     *
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private static Map<String, CellStyle> createStyles(Workbook wb) {
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();

        CellStyle style = wb.createCellStyle();
        style.setAlignment(ALIGN_CENTER);
        style.setVerticalAlignment(VERTICAL_CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
        style.setFont(titleFont);
        styles.put("title", style);

        style = wb.createCellStyle();
        style.setVerticalAlignment(VERTICAL_CENTER);
        style.setBorderRight(CellStyle.BORDER_THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(CellStyle.BORDER_THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(CellStyle.BORDER_THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(CellStyle.BORDER_THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(CellStyle.ALIGN_LEFT);
        styles.put("data1", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(ALIGN_CENTER);
        styles.put("data2", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(CellStyle.ALIGN_RIGHT);
        styles.put("data3", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
//		style.setWrapText(true);
        style.setAlignment(ALIGN_CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
        headerFont.setFontName("Arial");
        headerFont.setFontHeightInPoints((short) 10);
        headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);

        return styles;
    }
}

使用

@RequestMapping("exportSearchList")
    public void exportSearchList(HttpServletResponse response) throws IOException {
        // 初始化表头和数据
        List<String> headers = Arrays.asList("列1", "列2", "列3");
        List<List<Object>> data = new ArrayList<>();
        data.add(Arrays.asList("数据1", "数据2", "数据3"));
        data.add(Arrays.asList("数据4", "数据5", "数据6"));

        ExcelCustomExportUtil.exportExcel(response, headers, data);
    }
<think>在Spring Boot导出Excel时,动态表头通常意味着表头名称不是固定的,而是根据某些条件(如用户选择、数据库配置、国际化等)动态生成的。以下是一种常见的实现方案: ### 解决方案:使用Apache POI + 动态表头数据构建 #### 1. 添加依赖 ```xml <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Apache POI for Excel --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> <!-- 使用最新版本 --> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> </dependencies> ``` #### 2. 构建动态表头数据 通常,动态表头数据可以从数据库、配置文件或前端请求中获取。这里假设我们从前端接收一个表头列表。 ##### 请求参数示例(JSON): ```json { "headers": ["动态列1", "动态列2", "动态列3"], "data": [ ["值1", "值2", "值3"], ["值4", "值5", "值6"] ] } ``` #### 3. 创建导出工具类 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.ByteArrayOutputStream; import java.util.List; public class ExcelExporter { public static byte[] export(List<String> headers, List<List<String>> data) { try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("Sheet1"); // 创建表头行 Row headerRow = sheet.createRow(0); for (int i = 0; i < headers.size(); i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(headers.get(i)); } // 填充数据 for (int i = 0; i < data.size(); i++) { Row row = sheet.createRow(i + 1); // 从第二行开始 List<String> rowData = data.get(i); for (int j = 0; j < rowData.size(); j++) { Cell cell = row.createCell(j); cell.setCellValue(rowData.get(j)); } } // 自适应列宽(可选) for (int i = 0; i < headers.size(); i++) { sheet.autoSizeColumn(i); } // 写入字节数组 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); workbook.write(outputStream); return outputStream.toByteArray(); } catch (Exception e) { throw new RuntimeException("导出Excel失败", e); } } } ``` #### 4. 创建Spring Boot控制器 ```java import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class ExcelExportController { @PostMapping("/export") public ResponseEntity<byte[]> exportExcel(@RequestBody ExportRequest request) { // 获取动态表头和数据 List<String> headers = request.getHeaders(); List<List<String>> data = request.getData(); // 导出Excel byte[] excelBytes = ExcelExporter.export(headers, data); // 设置响应头 HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); responseHeaders.setContentDispositionFormData("attachment", "export.xlsx"); return ResponseEntity.ok() .headers(responseHeaders) .body(excelBytes); } // 内部类:请求体结构 public static class ExportRequest { private List<String> headers; private List<List<String>> data; // getters and setters public List<String> getHeaders() { return headers; } public void setHeaders(List<String> headers) { this.headers = headers; } public List<List<String>> getData() { return data; } public void setData(List<List<String>> data) { this.data = data; } } } ``` ### 动态表头的高级场景 #### 场景1:多语言表头 通过用户语言环境动态切换表头: ```java @PostMapping("/export") public ResponseEntity<byte[]> exportExcel( @RequestBody ExportRequest request, @RequestHeader("Accept-Language") String language) { // 根据语言获取表头翻译(示例) List<String> translatedHeaders = request.getHeaders().stream() .map(header -> translate(header, language)) .collect(Collectors.toList()); return exportResponse(translatedHeaders, request.getData()); } private String translate(String key, String lang) { // 实际实现中,这里可以调用多语言资源文件 if ("zh-CN".equals(lang)) { return key + "中文"; } else { return key; } } ``` #### 场景2:从数据库读取表头配置 ```java @Autowired private HeaderConfigRepository headerConfigRepository; @PostMapping("/export") public ResponseEntity<byte[]> exportExcel(@RequestBody ExportRequest request) { // 从数据库获取表头配置(假设根据模块名) List<HeaderConfig> configs = headerConfigRepository.findByModule("insurance"); List<String> headers = configs.stream() .map(HeaderConfig::getDisplayName) .collect(Collectors.toList()); // 注意:数据需要与表头顺序匹配 return exportResponse(headers, request.getData()); } ``` ### 优化:使用EasyExcel(阿里开源库) EasyExcel提供了更简洁的API和更好的性能(避免OOM)。 #### 1. 添加依赖 ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> </dependency> ``` #### 2. 使用EasyExcel动态导出 ```java import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.metadata.WriteSheet; import java.io.ByteArrayOutputStream; import java.util.List; public static byte[] easyExport(List<String> headers, List<List<String>> data) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { // 构建ExcelWriter ExcelWriter excelWriter = EasyExcel.write(outputStream).build(); // 动态构建表头 WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1") .head(headers.stream().map(Collections::singletonList).collect(Collectors.toList())) .build(); // 写入数据(数据格式为List<List<String>>) excelWriter.write(data, writeSheet); excelWriter.finish(); return outputStream.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } ``` ### 关键点说明 1. **表头与数据匹配**:动态表头需要确保数据列的顺序与表头完全对应。 2. **性能优化**:大数据量导出时使用`SXSSFWorkbook`(POI)或`EasyExcel`的分段导出。 3. **安全考虑**:验证表头和数据来源,防止注入攻击。 --- §§相关问题§§: 1. 如何在Spring Boot实现Excel导出的多语言支持? 2. 使用Apache POI导出大文件(百万行)时如何避免内存溢出? 3. EasyExcel相比Apache POI有哪些优势? 4. 如何实现动态合并单元格的Excel导出?</think># Spring Boot 导出Excel动态表头解决方案 在Spring Boot实现动态表头Excel导出,主要涉及动态生成表头列及其对应数据。以下是一个完整的解决方案: ## 解决方案:使用Apache POI + 动态配置 ### 1. 添加依赖 ```xml <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Apache POI for Excel --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` ### 2. 定义动态表头配置类 ```java @Data public class HeaderConfig { private String fieldName; // 实体类属性名 private String displayName; // Excel表头显示名称 private int width = 15; // 列宽(字符数) private CellStyle style; // 自定义样式(可选) } ``` ### 3. 创建Excel导出工具类 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Field; import java.net.URLEncoder; import java.util.List; @Component public class DynamicExcelExporter { public <T> void export(HttpServletResponse response, String fileName, List<HeaderConfig> headers, List<T> data) throws IOException { // 创建工作簿和工作表 Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); // 创建表头样式 CellStyle headerStyle = createHeaderStyle(workbook); // 创建表头行 Row headerRow = sheet.createRow(0); for (int i = 0; i < headers.size(); i++) { HeaderConfig config = headers.get(i); Cell cell = headerRow.createCell(i); cell.setCellValue(config.getDisplayName()); cell.setCellStyle(headerStyle); // 设置列宽 sheet.setColumnWidth(i, config.getWidth() * 256); } // 填充数据 for (int rowIndex = 0; rowIndex < data.size(); rowIndex++) { Row row = sheet.createRow(rowIndex + 1); T item = data.get(rowIndex); for (int colIndex = 0; colIndex < headers.size(); colIndex++) { HeaderConfig config = headers.get(colIndex); Cell cell = row.createCell(colIndex); try { // 使用反射获取属性值 Field field = item.getClass().getDeclaredField(config.getFieldName()); field.setAccessible(true); Object value = field.get(item); cell.setCellValue(value != null ? value.toString() : ""); } catch (Exception e) { cell.setCellValue(""); } } } // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); // 写入响应流 workbook.write(response.getOutputStream()); workbook.close(); } private CellStyle createHeaderStyle(Workbook workbook) { CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); return style; } } ``` ### 4. 创建控制器 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/api/excel") public class ExcelExportController { @Autowired private DynamicExcelExporter excelExporter; @GetMapping("/export") public void exportExcel(HttpServletResponse response) { // 1. 定义动态表头配置 List<HeaderConfig> headers = new ArrayList<>(); headers.add(new HeaderConfig("name", "姓名", 20)); headers.add(new HeaderConfig("age", "年龄", 10)); headers.add(new HeaderConfig("email", "电子邮箱", 30)); headers.add(new HeaderConfig("department", "部门", 25)); // 2. 准备数据(实际应用中从数据库获取) List<User> users = new ArrayList<>(); users.add(new User("张三", 30, "zhangsan@example.com", "技术部")); users.add(new User("李四", 28, "lisi@example.com", "市场部")); users.add(new User("王五", 35, "wangwu@example.com", "财务部")); // 3. 导出Excel try { excelExporter.export(response, "员工信息表", headers, users); } catch (IOException e) { throw new RuntimeException("导出Excel失败", e); } } // 示例实体类 @Data static class User { private String name; private int age; private String email; private String department; public User(String name, int age, String email, String department) { this.name = name; this.age = age; this.email = email; this.department = department; } } } ``` ## 高级功能:完全动态表头 ### 1. 从数据库加载表头配置 ```java @Service public class HeaderConfigService { @Autowired private HeaderConfigRepository repository; public List<HeaderConfig> getHeadersByReportType(String reportType) { return repository.findByReportTypeOrderByColumnOrder(reportType); } } // 在控制器中使用 @GetMapping("/export-dynamic") public void exportDynamicExcel(HttpServletResponse response, @RequestParam String reportType) { // 从数据库获取表头配置 List<HeaderConfig> headers = headerConfigService.getHeadersByReportType(reportType); // 获取对应数据 List<?> data = dataService.getDataByReportType(reportType); // 导出Excel excelExporter.export(response, reportType + "报表", headers, data); } ``` ### 2. 支持多级表头 ```java // 修改HeaderConfig类 @Data public class HeaderConfig { private String fieldName; private String displayName; private int width = 15; private int level = 1; // 表头层级 private String parent; // 父级表头名称 } // 在导出工具类中添加多级表头支持 private void createMultiLevelHeader(Sheet sheet, List<HeaderConfig> headers) { // 按层级分组 Map<Integer, List<HeaderConfig>> grouped = headers.stream() .collect(Collectors.groupingBy(HeaderConfig::getLevel)); // 创建各级表头行 int maxLevel = Collections.max(grouped.keySet()); for (int level = 1; level <= maxLevel; level++) { Row row = sheet.getRow(level - 1) != null ? sheet.getRow(level - 1) : sheet.createRow(level - 1); List<HeaderConfig> levelHeaders = grouped.get(level); for (HeaderConfig config : levelHeaders) { // 计算合并单元格范围 // 实现略... } } } ``` ### 3. 使用EasyExcel实现(更简单的方式) ```java // 添加依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> </dependency> // 使用EasyExcel实现动态导出 public void exportWithEasyExcel(HttpServletResponse response, String fileName, List<HeaderConfig> headers, List<?> data) throws IOException { // 构建表头 List<List<String>> head = new ArrayList<>(); for (HeaderConfig config : headers) { head.add(Collections.singletonList(config.getDisplayName())); } // 构建数据 List<List<Object>> dataList = new ArrayList<>(); for (Object item : data) { List<Object> rowData = new ArrayList<>(); for (HeaderConfig config : headers) { try { Field field = item.getClass().getDeclaredField(config.getFieldName()); field.setAccessible(true); rowData.add(field.get(item)); } catch (Exception e) { rowData.add(""); } } dataList.add(rowData); } // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); // 写入Excel EasyExcel.write(response.getOutputStream()) .head(head) .sheet("Sheet1") .doWrite(dataList); } ``` ## 关键点说明 1. **动态表头核心**:通过配置对象定义表头显示名称和对应的实体字段 2. **反射机制**:使用反射动态获取实体属性值 3. **样式定制**:可自定义表头样式、列宽等 4. **数据源分离**:表头配置和数据获取完全解耦 5. **扩展性**:支持从数据库、配置文件或API获取表头配置 ## 最佳实践建议 1. **缓存配置**:频繁使用的表头配置可以缓存到Redis 2. **国际化**:根据用户语言返回不同表头显示名称 3. **性能优化**:大数据量导出使用分页查询+SXSSFWorkbook 4. **安全性**:验证表头配置来源,防止反射攻击 5. **错误处理**:添加详细的日志记录和异常处理 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值