【经验分享】EasyExcel实现自定义动态化导出excel

需求:

需求涉及保存设备的灵活属性,设备的属性并不固定。允许设备的属性动态扩展。最终数据库采用EAV 模型的设计模式,使用三张表实现:设备表 、属性定义表、属性值表。

现在堆上述数据进行导入和导出。

解决:

这里使用EasyExcel,处理数据的导入导出。工具类核心方法如下:

java 复制代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Collectors;

/**
     * 自定义导出
     * @param os
     * @param titleList
     * @param mergeTitles
     * @param dataList
     */
    public static void customDynamicExport(OutputStream os, List<String> titleList,
                                           List<String> mergeTitles, List<List<Object>> dataList) {
//        fileName = fileName + ".xlsx";

        // 导出的数据转换为Map数据结构, k -> 标题,v -> 此标题下的数据按顺序
        Map<String, List<Object>> dataMap = new HashMap<>(titleList.size());
        for (int i = 0 ; i < titleList.size(); i++) {
            // 当前列数据
            List<Object> currectTitleDatas = new ArrayList<>();
            for (int j = 0; j < dataList.size(); j++) {
                currectTitleDatas.add(dataList.get(j).get(i));
            }
            dataMap.put(titleList.get(i), currectTitleDatas);
        }

        // **标题信息
        List<List<String>> headTitleInfo = excelTitle(titleList);
        // **数据信息
        List<List<Object>> excelDataInfo = excelData(dataList);

        // **合并单元格信息
        List<LoopMergeStrategy> mergeStrategies = mergeCells(titleList, mergeTitles, dataMap);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(os);
        if (!CollectionUtils.isEmpty(mergeStrategies)) {
//            log.info("开始进行合并单元格操作 mergeStrategies ===> {}", mergeStrategies);
            mergeStrategies.forEach(m -> {
                excelWriterBuilder.registerWriteHandler(m);
            });
        }

        excelWriterBuilder.head(headTitleInfo).sheet("模板")
            // 当然这里数据也可以用 List<List<String>> 去传入
            .doWrite(excelDataInfo);
    }

    /**
     * **excel标题
     *
     * @param titleList
     * @return
     */
    private static List<List<String>> excelTitle(List<String> titleList) {

        List<List<String>> excelTitleList = new ArrayList<>(titleList.size());
        titleList.forEach(k -> {
            List<String> titles = new ArrayList<>(1);
            titles.add(k);
            excelTitleList.add(titles);
        });
        return excelTitleList;
    }

    private static List<List<Object>> excelData(List<List<Object>> dataList) {
        return dataList;
    }

    private static List<LoopMergeStrategy> mergeCells(List<String> titleList, List<String> mergeTitles, Map<String, List<Object>> dataMap) {

        // 将需要合并的项转换为Map,以便**合并位置
        Map<String, String> mergeTitleMap = mergeTitles.stream().collect(Collectors.toMap(k -> k, k -> k));

        // **合并位置
        Map<String, Integer> mergeLocationMap = new HashMap<>(mergeTitles.size());
        for (int i = 0; i < titleList.size(); i++) {
            if (Objects.nonNull(mergeTitleMap.get(titleList.get(i)))) {
                mergeLocationMap.put(titleList.get(i), i);
            }
        }

        List<LoopMergeStrategy> mergeStrategyList = new ArrayList<>();

        mergeTitles.forEach(k -> {
            // 合并数据所在的列数
            Integer columnIndex = mergeLocationMap.get(k);

            List<Object> mergeDatas = dataMap.get(k);

            // 需要合并的数量
            Integer currectMergeNum = 1;
            for (int m = 0; m < mergeDatas.size(); m++) {
                // 是否存在合并环节
                Boolean isMerge = false;
                if (m != 0 && Objects.equals(mergeDatas.get(m), mergeDatas.get(m - 1))) {
                    isMerge = true;
                    currectMergeNum = currectMergeNum + 1;

                    if (m == mergeDatas.size() - 1) {
                        isMerge = false;
                    }
                }
                if (!isMerge && currectMergeNum > 1) {
                    // 合并的行数
                    Integer eachRow = currectMergeNum;
                    mergeStrategyList.add(new LoopMergeStrategy(eachRow, columnIndex));
                    currectMergeNum = 1;
                }
            }
        });
        return mergeStrategyList;
    }
相关推荐
张小姐的猫1 天前
【MySQL】访问 & 数据流动原理
android·数据库·mysql·adb·1024程序员节
工业HMI实战笔记2 天前
印刷电路板HMI:SMT贴装的元件定位界面
制造·设计规范·设计语言·1024程序员节
Martina_03213 天前
AI生成3D模型一合并网格,材质就串色?用6步检查材质槽、UV与图集重映射
图像处理·人工智能·3d·材质·uv·游戏策划·1024程序员节
橙光成长3 天前
android-ad-cleaner:android免 Root 批量关闭系统开屏与营销推送
android·1024程序员节
燃烧的枫4 天前
Windows 下 YOLO11 Pose 的 TensorRT C++ 部署
windows·yolo·tensorrt·1024程序员节·yolo11-pose
AI日报派送佬4 天前
2026年11月02日 AI 行业日报|商汤开源 Loop-9B、Pika 发布 H3 Flex 音视频模型、Meta× 傅利叶推出人形机器人 Max
人工智能·微软·开源·音视频·openai·1024程序员节·ai日报
avi91114 天前
【AI教人】UnityEditor扩展Spine动画,Spine骨骼,预览Preview一切
spine·骨骼动画·1024程序员节·editor扩展·spine动画·spine骨骼
开开心心就好4 天前
网页图片批量下载工具,输入链接就保存
服务器·算法·leetcode·支持向量机·pdf·ocr·1024程序员节
Martina_03214 天前
AI生成的3D模型打印后沿层纹开裂?用6步检查方向、壁厚与填充
图像处理·人工智能·数学建模·3d·自然语言处理·游戏策划·1024程序员节