Java 导出 (动态导出,表头和Value动态获取)

本文介绍了一种根据业务需求动态导出Excel数据的方法,通过解析不同的表单数据,自动生成适应的表头和数据内容,适用于没有固定表头和值的场景。

项目上业务需求,导出数据为动态数据,没有固定表头,没有固定值,每个表单下导出得数据都不同,普通导出无法实现,下面我就给大家展示我得做法:

下面是我的Controller写法:

     注意的是:根据Hashmap获取不同数据在根据key value区分开,从新拼接,主要是看我的工具类,大家可以学习,希望能帮助你们‘

   /**
     * 表单导出
     * 
     * @param response
     * @param export
     * @param request
     */
    @RequestMapping("/fForm/toExportExcel")
    public void exportExcel(HttpServletRequest request, HttpServletResponse response) {
        try {
            Map<String, Object> paramMap = RequestUtils.convertRequestToMap(request);
            Long formId = getLong(paramMap, "formId");
            // 姓名 手机号条件查询
            String condition = getString(paramMap, "condition");
            // 表头
            ArrayList<String> excelHeader = new ArrayList<String>();
            List<Map<String, Object>> list1 = fFormService.findFFromFliedConfig(formId);
            excelHeader.add("序号");
            for (Map<String, Object> map : list1) {
                String showName = map.get("showName").toString();
                excelHeader.add(showName);
            }
            excelHeader.add("提交时间");
            String[] arrString = (String[]) excelHeader.toArray(new String[excelHeader.size()]);
            // 数据
            List<Map<String, Object>> fromDataList = fFormService.findFFormData1(formId, condition);
            List<Object[]> dataList = new ArrayList<Object[]>();
            ArrayList<String> datal = null;
            for (Map<String, Object> map : fromDataList) {
                datal = new ArrayList<String>();
                datal.add(map.get("id").toString());
                if (map.get("sex") == null) {
                    map.put("sex", "");
                } else if (map.get("sex").equals("1")) {
                    map.put("sex", "男");
                } else if (map.get("sex").equals("2")) {
                    map.put("sex", "女");
                }
                for (Map<String, Object> map1 : list1) {
                    String dataName = String.valueOf(map1.get("dataName"));
                    String data = String.valueOf(map.get(dataName));
                    if (data == "null") {
                        datal.add("");
                    } else {
                        datal.add(data);
                    }
                }
                SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm :ss");
                datal.add(String.valueOf(s.format(map.get("commit_time"))));
                Object[] array = datal.toArray(new String[datal.size()]);
                dataList.add(array);
            }
            ExportExcel.exportExcel(request, response, "表单导出", arrString, dataList, "yyyy-MM-dd HH:mm :ss");
        } catch (

        Exception e) {
            e.printStackTrace();
        }
    }

导出工具类:

/**
 * 动态导出工具( 根据数据列动态获取集合中数据)
 * 
 * @author v_guoyunlong
 *
 */
public class ExportExcel {

    /**
     * 
     * 
     * @param fileName 文件名
     * @param headers 表格属性列名数组
     * @param dataset 需要显示的数据集合
     * @param pattern 如果有时间数据,设定输出格式。默认为"yyy-MM-dd"
     * @param file1
     * @param zc2
     * @param file
     * @param file
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public static HSSFWorkbook exportExcel(HttpServletRequest request, HttpServletResponse response, String fileName,
            String[] headers, List<Object[]> dataset, String pattern) throws Exception {
        // 设置请求
        response.setHeader("content-disposition",
                String.format("attachment;filename*=utf-8'zh_cn'%s.xls", URLEncoder.encode(fileName, "utf-8")));
        response.setContentType("application/msexcel;charset=UTF-8");
        // 创建一个工作薄
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 生成一个表格
        HSSFSheet sheet = workbook.createSheet("表单导出");
        // 产生表格标题行
        HSSFRow row = sheet.createRow(0);
        for (short i = 0; i < headers.length; i++) {
            HSSFCell cell = row.createCell(i);
            HSSFRichTextString text = new HSSFRichTextString(headers[i]);
            cell.setCellValue(text);
        }

        // 遍历集合数据,产生数据行
        Iterator<Object[]> it = dataset.iterator();
        int index = 0;
        while (it.hasNext()) {
            index++;
            // 从第1行开始创建
            row = sheet.createRow(index);
            Object[] obj = (Object[]) it.next();
            for (short i = 0; i < obj.length; i++) {
                HSSFCell cell = row.createCell(i);
                Object value = obj[i];
                String textValue = null;
                if (!"".equals(value) && value != null) {
                    if (value instanceof Integer) {
                        int intValue = (Integer) value;
                        cell.setCellValue(intValue);
                    } else if (value instanceof Float) {
                        float fValue = (Float) value;
                        cell.setCellValue(fValue);
                    } else if (value instanceof Double) {
                        double dValue = (Double) value;
                        cell.setCellValue(dValue);
                    } else if (value instanceof Long) {
                        long longValue = (Long) value;
                        cell.setCellValue(longValue);
                    } else if (value instanceof Date) {
                        Date date = (Date) value;
                        if (null == pattern || pattern.equals("")) {
                            pattern = "yyyy-MM-dd";
                        }
                        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
                        textValue = sdf.format(date);
                        cell.setCellValue(textValue);
                    } else {
                        // 其它数据类型都当作字符串简单处理
                        textValue = value.toString();
                        // 设置单元格的值
                        cell.setCellValue(textValue);
                    }
                } else {
                    cell.setCellValue("");
                }
            }

        }

        OutputStream outputStream = response.getOutputStream();// 打开流
        // ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        // // 压缩包名
        // String fileNames = "表单压缩文件" + ".zip";
        // response.setContentType("application/octet-stream ");
        // // 表示不能用浏览器直接打开
        // response.setHeader("Connection", "close");
        // // 告诉客户端允许断点续传多线程连接下载
        // // response.setHeader("Accept-Ranges", "bytes");
        // response.setHeadear("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileNames, "utf-8"));
        // response.setCharacterEncoding("UTF-8");
        // // 创建一个zip实体
        // ZipEntry entry = new ZipEntry(fileName + ".xls");
        // zipOutputStream.putNextEntry(entry);
        // workbook.write(zipOutputStream);
        // // 刷新 关闭输出流
        // zipOutputStream.flush();
        // zipOutputStream.close();
        // 让列宽随着导出的列长自动适应
        for (int colNum = 0; colNum < headers.length; colNum++) {
            int columnWidth = sheet.getColumnWidth(colNum) / 256;
            for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
                HSSFRow currentRow;
                // 当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }
                if (currentRow.getCell(colNum) != null) {
                    HSSFCell currentCell = currentRow.getCell(colNum);
                    if (currentCell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
                        int length = currentCell.getStringCellValue() != null
                                ? currentCell.getStringCellValue().getBytes().length : 10;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            if (colNum == 0) {
                sheet.setColumnWidth(colNum, (columnWidth - 2) * 256);
            } else {
                sheet.setColumnWidth(colNum, (columnWidth + 4) * 256);
            }
        }
        // HSSFWorkbook写入流
        workbook.write(outputStream);
        // HSSFWorkbook关闭
        workbook.close();
        // 刷新流
        outputStream.flush();
        // 关闭流
        outputStream.close();
        return workbook;
    }

最后说的是,还是根据自己项目中的业务需求去写,工具类是通用,谢谢

<think>我们正在处理一个关于Java实现动态表头导出Excel的需求。根据提供的引用,我们可以了解到动态表头导出Excel的关键点在于表头不是固定的,而是根据后台数据动态生成的。我们需要从接口获取表头信息,然后根据这些信息构建Excel的表头,并将数据填充到对应的列中。参考引用内容: -引用[1][2]提到表头数据由后台接口提供,因此不能写死表头,需要动态构建。 -引用[3][4]提供了具体的实现思路代码示例,括使用POI或EasyPOI库来实现。实现步骤:1.定义表头实体类:通常括字段名(field)字段显示名称(fieldName)等。 2.获取动态表头数据:通过接口获取表头列表(List<CustomFieldVo>)。 3.获取数据列表:通过接口获取需要导出的数据列表(List<EmpBasicExcelVo>),注意每条数据中可能动态字段的值。4.构建Excel:a.创建Workbook对象(HSSFWorkbook或XSSFWorkbook)。b.创建Sheet。c.创建表头行(Row),根据动态表头列表循环创建单元格(Cell),并设置单元格的值。d.遍历数据列表,为每一行数据创建行,然后根据表头字段顺序,将数据填充到对应的单元格中。注意:在引用[3]中提到,数据需要纵横转换。因为通常我们的数据是一条记录对应一行,而动态表头则要求每一列对应一个动态字段。所以,在填充数据时,需要根据表头中的字段名(field)从数据对象中获取对应的值。 另外,引用[4]提到了使用EasyPOI库,它提供了注解方式导出,但动态表头可能需要更灵活的处理。我们可以选择使用原生的POI,也可以结合EasyPOI的特性(如定义注解)来实现。这里我们选择使用原生的ApachePOI来实现,因为动态表头需要灵活构建,原生POI控制更细致。 具体代码步骤: 1.引入POI依赖(Maven): ```xml<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> ```2.代码实现:假设我们有两个实体类:```java//自定义表头字段publicclass CustomFieldVo{private Stringfield;//字段标识(如:name,age)privateString fieldName;//表头显示名称(如:姓名,年龄)//省略getter/setter} //数据对象publicclass EmpBasicExcelVo {//固定字段privateString name;private StringidCard;//动态字段:用一个Map来存储,key为字段标识,value为字段值privateMap<String, Object> dynamicFields;//省略其他字段getter/setter}``` 导出工具类方法:```javaimportorg.apache.poi.ss.usermodel.*; importorg.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.OutputStream;import java.util.List; publicclass ExcelExportUtil{public staticvoid exportExcel(List<CustomFieldVo> headList,List<EmpBasicExcelVo> dataList,OutputStreamout){//1.创建一个工作簿Workbookworkbook =new XSSFWorkbook();Sheetsheet =workbook.createSheet("Sheet1");//2.创建表头行RowheadRow =sheet.createRow(0);for(inti =0; i< headList.size(); i++) {Cellcell =headRow.createCell(i);cell.setCellValue(headList.get(i).getFieldName());}//3.填充数据行for(inti=0;i <dataList.size();i++){Row row= sheet.createRow(i +1); //表头占第一行EmpBasicExcelVo emp= dataList.get(i);for(intj =0;j <headList.size();j++){Cell cell= row.createCell(j);Stringfield =headList.get(j).getField();//根据字段名获取值Object value= null;//如果是固定字段,则从emp对象中获取;如果是动态字段,则从dynamicFields中获取//这里假设固定字段也在dynamicFields中,或者分开处理。为了简化,我们可以将固定字段也放入dynamicFields,或者分开处理。//方法1:将固定字段也作为动态字段处理,即都放在dynamicFields中//方法2:单独判断固定字段,这里我们使用方法2,因为固定字段有明确的getter方法if("name".equals(field)) {value= emp.getName();}else if("idCard".equals(field)){value =emp.getIdCard();}else {//动态字段if(emp.getDynamicFields()!= null) {value= emp.getDynamicFields().get(field);}}//设置单元格值if (value !=null){cell.setCellValue(value.toString());} else{cell.setCellValue("");}}}//4.写入输出流try{workbook.write(out);workbook.close();}catch (Exception e) {e.printStackTrace();}}} ```注意:上述代码中,我们假设固定字段(name, idCard)动态字段都通过表头字段标识来匹配。如果表头字段标识是固定字段,则从对象的固定属性获取;否则从dynamicFields这个Map中获取。这样设计可以同时支持固定字段动态字段。 3.调用示例: ```java//获取动态表头列表List<CustomFieldVo> headList =getCustomFieldList();//获取数据列表List<EmpBasicExcelVo> dataList =getDataList();//输出流(例如HttpServletResponse的输出流) OutputStreamout =response.getOutputStream();ExcelExportUtil.exportExcel(headList, dataList,out);``` 以上是一个基本的实现。如果需要更复杂的操作(如样式设置、字典值替换等),可以在创建单元格时添加样式,或者在设置值之前进行值转换(例如将代码值转换成字典值)。字典值替换:可以在CustomFieldVo中添加一个字典类型,然后根据字典将存储的代码值转换成显示值。在设置单元格值的时候,先判断该字段是否需要字典转换,然后进行转换。 扩展:如果使用EasyPOI,可以简化很多操作,但需要按照EasyPOI的注解方式定义实体类。由于我们的表头动态的,使用原生POI更灵活。 总结:动态表头导出的核心是表头数据都是动态获取的,通过循环表头列表来构建表头行,然后根据表头字段标识从数据对象中获取对应的值填充到单元格中。 §§相关问题§§1.如何优化大量数据导出时的内存占用? 2.动态表头导出时如何设置单元格样式? 3.如何实现字典值的转换(例如将状态码0/1转换成“正常”/“禁用”)? 4.使用ApachePOIEasyPOI在动态表头导出上各有什么优缺点?</think>### Java实现动态表头导出Excel数据 在Java中实现动态表头导出Excel的核心思路是:**表头结构由后端动态生成**,而非前端写死。这需要解决两个关键问题: 1. **动态表头构建**:根据业务需求灵活生成表头结构 2. **数据动态映射**:将数据按表头字段对应填充 #### 实现方案(基于Apache POI) ```java // 1. 定义动态表头实体 public class DynamicHeader { private String fieldName; // 表头显示名称(如"姓名") private String fieldKey; // 字段标识(如"name") // 省略getter/setter } // 2. 核心导出方法 public void exportDynamicExcel(HttpServletResponse response, List<DynamicHeader> headers, List<Map<String, Object>> dataList) { try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("数据导出"); // 创建动态表头行 Row headerRow = sheet.createRow(0); for (int i = 0; i < headers.size(); i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(headers.get(i).getFieldName()); } // 填充动态数据 for (int rowIdx = 0; rowIdx < dataList.size(); rowIdx++) { Row dataRow = sheet.createRow(rowIdx + 1); Map<String, Object> rowData = dataList.get(rowIdx); for (int colIdx = 0; colIdx < headers.size(); colIdx++) { String fieldKey = headers.get(colIdx).getFieldKey(); Object value = rowData.get(fieldKey); dataRow.createCell(colIdx).setCellValue(value != null ? value.toString() : ""); } } // 输出Excel response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=export.xlsx"); workbook.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException("导出失败: " + e.getMessage()); } } ``` #### 实现要点说明 1. **动态表头结构** - 通过`DynamicHeader`对象定义表头字段 - 支持N级表头(需递归创建合并单元格) - 示例数据结构: ```json [ {"fieldName": "姓名", "fieldKey": "name"}, {"fieldName": "年龄", "fieldKey": "age"}, {"fieldName": "部门", "fieldKey": "dept"} ] ``` 2. **数据源格式** - 使用`List<Map<String, Object>>`存储数据 - 每个Map的key需与表头的fieldKey对应 - 示例数据: ```java List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row1 = new HashMap<>(); row1.put("name", "张三"); row1.put("age", 28); row1.put("dept", "研发部"); ``` 3. **性能优化** - 大数据量导出使用`SXSSFWorkbook`(流式导出) - 分批读取数据库数据(避免OOM) - 启用缓存样式减少内存占用 #### 扩展功能实现 1. **字典值转换**(如性别0/1转文字): ```java // 在DynamicHeader中添加转换函数 private Function<Object, String> valueConverter; // 填充数据时调用 if (header.getValueConverter() != null) { cellValue = header.getValueConverter().apply(value); } ``` 2. **多级表头实现**: ```java // 递归创建表头 void createMultiLevelHeader(Sheet sheet, List<HeaderNode> nodes, int startRow, int startCol) { for (HeaderNode node : nodes) { // 创建合并单元格 // 递归处理子节点 } } ``` #### 推荐工具库 1. **EasyPOI**(简化注解配置)[^4]: ```java @ExcelCollection(name = "动态表头") private List<DynamicColumn> columns; ``` 2. **Apache POI**(原生控制更灵活)[^3] > 提示:实际项目中建议将表头结构数据拆分为两个独立接口返回,前端分别请求后再组装导出[^1][^2]。 --- ###
评论 22
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭优秀的笔记

你的支持就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值