当前位置: 首页 > news >正文

Java按模板导出Excel

一、使用

1. 创建Excel模板,如图:

 2. 使用工具导出,代码示例:

 1 @GetMapping("/excel")2 public void exportExcel(HttpServletResponse response) throws Exception {3     List<Equipment> equipmentList = Arrays.asList(4             new Equipment("001", "设备A", "区域1", "仓库X"),5             new Equipment("002", "设备B", "区域2", "仓库Y"),6             new Equipment("003", "设备C", "区域3", "仓库Z")7     );8 9     List<Map<String, Object>> tempDataList = MapObjectUtil.objListToMapList(equipmentList);
10     List<DynamicDataMapping> dynamicDataMappingList = DynamicDataMapping.createOneDataList("equipment", tempDataList);
11     ExcelTemplateProc.doExportExcelByTemplateProc("excel-template/equipment.xlsx", response, null, dynamicDataMappingList);
12 }
13 
14 
15 
16 @Data
17 @AllArgsConstructor
18 class Equipment {
19     private String id;
20     private String name;
21     private String area;
22     private String wh;
23 }

二、工具类

1. 数据格式转换工具类:

 1 import java.lang.reflect.Field;2 import java.util.ArrayList;3 import java.util.HashMap;4 import java.util.List;5 import java.util.Map;6 7 public class MapObjectUtil {8 9     /**
10     * @description: 将object的list数据 转换成 map的list(如:List<Map<String, Object>>)
11     * @param objDataList
12     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
13     */
14     public static List<Map<String, Object>> objListToMapList(List<?> objDataList){
15         List<Map<String, Object>> dataList = new ArrayList<>();
16         if (objDataList==null || objDataList.size()<1){
17             return null;
18         }
19         objDataList.forEach(obj->{
20             try {
21                 Map<String, Object> map = MapObjectUtil.objectToMap(obj);
22                 dataList.add(map);
23             } catch (IllegalAccessException e) {
24                 throw new RuntimeException(e);
25             }
26         });
27         return dataList;
28     }
29 
30     /**
31     * @description: 将object数据转换成map数据
32     * @param obj
33     * @return java.util.Map<java.lang.String,java.lang.Object>
34     */
35     public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
36         Map<String, Object> map = new HashMap();
37         Class<?> cla = obj.getClass();
38         Field[] fields = cla.getDeclaredFields();
39         for (Field field : fields) {
40             field.setAccessible(true);
41             String keyName = field.getName();
42             Object value = field.get(obj);
43             if (value == null)
44                 value = "";
45             map.put(keyName, value);
46         }
47         return map;
48     }
49 
50 }

2. 存放导出动态数据类:

 1 import lombok.Data;2 3 import java.util.ArrayList;4 import java.util.Collections;5 import java.util.List;6 import java.util.Map;7 8 /**9  * @Description 存放需要导出的动态数据
10  */
11 @Data
12 public class DynamicDataMapping {
13 
14     private String dataId;
15     private List<Map<String, Object>> dataList;
16 
17 
18     /**
19     * @description: 组装只有一个list类型的动态数据
20     * @param dataId
21     * @param dataList
22     * @return
23     */
24     public static List<DynamicDataMapping> createOneDataList(String dataId, List<Map<String, Object>> dataList) {
25         if (dataList == null)
26             return null;
27         return Collections.singletonList(getDynamicDataMapping(dataId,dataList));
28     }
29 
30     /**
31     * @description: 组装只有多个list类型的动态数据
32     * @param transMap
33     * @return
34     */
35     public static List<DynamicDataMapping> createMorDataList(Map<String,List<Map<String, Object>>> transMap) {
36         if (transMap == null)
37             return null;
38         List<DynamicDataMapping> list = new ArrayList<>();
39         transMap.forEach((dataId,dataList)->{
40             list.add(getDynamicDataMapping(dataId,dataList));
41         });
42         return list;
43     }
44 
45     public static DynamicDataMapping getDynamicDataMapping(String dataId, List<Map<String, Object>> dataList){
46         DynamicDataMapping dynamicData = new DynamicDataMapping();
47         dynamicData.dataId = dataId;
48         dynamicData.dataList = dataList;
49         return dynamicData;
50     }
51 
52 } 

3. 导出Excel工具类:

  1 import jakarta.servlet.http.HttpServletResponse;2 import org.apache.poi.ss.usermodel.*;3 import org.apache.poi.xssf.usermodel.XSSFCell;4 import org.apache.poi.xssf.usermodel.XSSFRow;5 import org.apache.poi.xssf.usermodel.XSSFSheet;6 import org.apache.poi.xssf.usermodel.XSSFWorkbook;7 8 import java.io.*;9 import java.util.List;10 import java.util.Map;11 12 /**13  * @Description 根据模版导出Excel程序14  */15 public class ExcelTemplateProc {16 17     /**18      * @param templateFileName19      * @param20      * @param staticDataMap21      * @param dynamicDataMappingList22      * @return void23      * @description: 根据模版导出Excel入口24      */25     public static void doExportExcelByTemplateProc(String templateFileName, HttpServletResponse response,26                                                    Map<String, Object> staticDataMap,27                                                    List<DynamicDataMapping> dynamicDataMappingList) throws IOException {28         /**29          * 1. 从resources下加载模板并替换30          *    使用 ResourceUtils 加载文件31          */32         InputStream inputStream = ExcelTemplateProc.class.getClassLoader().getResourceAsStream(templateFileName);33        Workbook workbook = dealFirstSheetByTemplate(inputStream, staticDataMap, dynamicDataMappingList);34         // 替换里信息35 //        extracted(workbook);36         // 2. 反遣给前端37         try {38             //设置响应头39             response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");40             response.setHeader("Content-Disposition", "attachment; filename=" + templateFileName);41             // 输出流42             OutputStream outputStream = response.getOutputStream();43             workbook.write(outputStream);44             outputStream.flush();45         } finally {46             // Clean up resources47             if (workbook != null) {48                 workbook.close();49             }50             if (inputStream != null) {51                 inputStream.close();52             }53         }54     }55 56     private static void extracted(Workbook workbook) {57         Sheet sheet = workbook.getSheetAt(0); // 获取第一张表58         for (Row row : sheet) {59             for (Cell cell : row) {60                 if (cell.getCellType() == CellType.STRING) {61                     String cellValue = cell.getStringCellValue();62                     if ("已打卡".equals(cellValue) || "申诉成功".equals(cellValue)) {63                         // 替换成绿色对勾64                         CellStyle greenCheckStyle = workbook.createCellStyle();65                         Font greenFont = workbook.createFont();66                         greenFont.setColor(IndexedColors.GREEN.getIndex());67                         greenFont.setBold(true);68                         greenCheckStyle.setFont(greenFont);69                         cell.setCellValue("✓"); // 使用绿色对勾的替换字符70                         cell.setCellStyle(greenCheckStyle);71                     } else if (cellValue == null || "".equals(cellValue) || "申诉驳回".equals(cellValue)) {72                         // 替换成红色叉号73                         CellStyle redCrossStyle = workbook.createCellStyle();74                         Font redFont = workbook.createFont();75                         redFont.setColor(IndexedColors.RED.getIndex());76                         redFont.setBold(true);77                         redCrossStyle.setFont(redFont);78                         cell.setCellValue("✗"); // 使用红色叉号的替换字符79                         cell.setCellStyle(redCrossStyle);80                     } else if ("未签退".equals(cellValue)) {81                         // 替换成绿色对勾82                         CellStyle greenCheckStyle = workbook.createCellStyle();83                         Font yellowFont = workbook.createFont();84                         yellowFont.setColor(IndexedColors.ORANGE.getIndex());85                         yellowFont.setBold(true);86                         greenCheckStyle.setFont(yellowFont);87                         cell.setCellValue("✓"); // 使用橘色对勾的替换字符88                         cell.setCellStyle(greenCheckStyle);89                     } else if ("请假".equals(cellValue)) {90                         // 替换成黄色对勾91                         CellStyle greenCheckStyle = workbook.createCellStyle();92                         Font yellowFont = workbook.createFont();93                         yellowFont.setColor(IndexedColors.YELLOW.getIndex());94                         yellowFont.setBold(true);95                         greenCheckStyle.setFont(yellowFont);96                         cell.setCellValue("⚪"); // 使用绿色对勾的替换字符97                         cell.setCellStyle(greenCheckStyle);98                     }99                     else if ("申诉中".equals(cellValue)) {
100                         // 替换成宗色对勾
101                         CellStyle greenCheckStyle = workbook.createCellStyle();
102                         Font yellowFont = workbook.createFont();
103                         yellowFont.setColor(IndexedColors.BROWN.getIndex());
104                         yellowFont.setBold(true);
105                         greenCheckStyle.setFont(yellowFont);
106                         cell.setCellValue("🔺"); // 使用zong色对勾的替换字符
107                         cell.setCellStyle(greenCheckStyle);
108                     }
109                 }
110             }
111         }
112     }
113 
114     /**
115      * @param workbook
116      * @param excelFilePath
117      * @return void
118      * @description: 保存导出的Excel文件到服务器
119      */
120     public static void saveExportFile(Workbook workbook, String excelFilePath) throws IOException {
121         FileOutputStream outputStream = new FileOutputStream(excelFilePath);
122         executeWorkBookWrite(workbook, outputStream);
123     }
124 
125     /**
126      * @param workbook
127      * @param outputStream
128      * @return void
129      * @description: 数据输出
130      */
131     public static void executeWorkBookWrite(Workbook workbook, OutputStream outputStream) throws IOException {
132         workbook.write(outputStream);
133         outputStream.flush();
134         outputStream.close();
135         workbook.close();
136     }
137 
138     /**
139      * @param inputStream
140      * @param staticDataMap
141      * @param dynamicDataMappingList
142      * @return org.apache.poi.ss.usermodel.Workbook
143      * @description: 处理只有一个sheet页的模版
144      */
145     public static Workbook dealFirstSheetByTemplate(InputStream inputStream,
146                                                     Map<String, Object> staticDataMap,
147                                                     List<DynamicDataMapping> dynamicDataMappingList) throws IOException {
148         XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
149         XSSFSheet sheet = workbook.getSheetAt(0);
150         // 按模板处理sheet页
151         dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList);
152         return workbook;
153     }
154 
155     /**
156      * @param sheet
157      * @param staticDataMap
158      * @param dynamicDataMappingList
159      * @return void
160      * @description: 按模板处理sheet页里的数据
161      */
162     private static void dealSheetDataByTemplate(XSSFSheet sheet, Map<String, Object> staticDataMap, List<DynamicDataMapping> dynamicDataMappingList) {
163         // 循环sheet里每一行
164         for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
165             XSSFRow row = sheet.getRow(i);
166             DynamicDataMapping dynamicDataMapping = getDynamicRowDataByMatch(row, dynamicDataMappingList);
167             if (dynamicDataMapping != null) {
168                 i = getTemplateLastRowIndexAfterDealTemplate(sheet, i, dynamicDataMapping);
169             } else {
170                 dealTemplateDataRow(row, null, staticDataMap);
171             }
172         }
173     }
174 
175     /**
176      * @param row
177      * @param dataMap
178      * @param dataPrefix
179      * @return void
180      * @description: 循环处理模版中每行的数据
181      */
182     private static void dealTemplateDataRow(XSSFRow row, String dataPrefix, Map<String, Object> dataMap) {
183         if (dataMap == null) {
184             return;
185         }
186         for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
187             XSSFCell cell = row.getCell(i);
188             fillInTemplateCellDataValue(cell, dataPrefix, dataMap);
189         }
190     }
191 
192     /**
193      * @param cell
194      * @param dataPrefix
195      * @param dataMap
196      * @return void
197      * @description: 填充模版里单元格的值
198      */
199     private static void fillInTemplateCellDataValue(XSSFCell cell, String dataPrefix, Map<String, Object> dataMap) {
200         if (cell == null) {
201             return;
202         }
203         String cellValue = cell.getStringCellValue(); // 获取模版里设置的数据
204         if (cellValue == null || cellValue.trim().isEmpty()) {
205             return;
206         }
207         boolean flag = false;
208         dataPrefix = (dataPrefix == null || dataPrefix.isEmpty()) ? "" : (dataPrefix + ".");
209         for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
210             // 循环所有,因为可能一行有多个占位符
211             String cellTemplateStr = "{{" + dataPrefix + entry.getKey() + "}}";
212             if (cellValue.contains(cellTemplateStr)) {
213                 // 替换模版中单元格的数据
214                 cellValue = cellValue.replace(cellTemplateStr, entry.getValue() == null ? "" : entry.getValue().toString());
215                 flag = true;
216             }
217         }
218         if (flag) {
219             cell.setCellValue(cellValue);
220         }
221     }
222 
223     /**
224      * @param row
225      * @param dynamicDataMappingList
226      * @return com.liu.susu.excel.template.poi.common.DynamicDataMapping
227      * @description: 通过模版sheet中的行数据 与 动态数据匹配,获取此行需要填充的动态数据
228      */
229     private static DynamicDataMapping getDynamicRowDataByMatch(XSSFRow row, List<DynamicDataMapping> dynamicDataMappingList) {
230         if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) {
231             return null;
232         }
233         if (row != null) {
234             for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
235                 XSSFCell cell = row.getCell(j);
236                 CellType cellType = cell.getCellType();
237                 switch (cellType) {
238                     case STRING:
239                         String value = cell.getStringCellValue();
240                         if (value != null) {
241                             for (DynamicDataMapping dynamicData : dynamicDataMappingList) {
242                                 if (value.startsWith("{{" + dynamicData.getDataId() + ".")) {
243                                     return dynamicData;
244                                 }
245                             }
246                         }
247                         ;
248                         break;
249                 }
250 
251             }
252         }
253         return null;
254 
255     }
256 
257     /**
258      * @param sheet
259      * @param rowIndex
260      * @param dynamicDataMapping
261      * @return int
262      * @description: 根据动态数据的条数动态复制模版行,每处理一个类型的list返回最后的行数,进而处理下一个类型的list
263      */
264     private static int getTemplateLastRowIndexAfterDealTemplate(XSSFSheet sheet, int rowIndex, DynamicDataMapping dynamicDataMapping) {
265         if (dynamicDataMapping == null) {
266             return rowIndex;
267         }
268         int dataRows = dynamicDataMapping.getDataList().size();
269         // 需要拷贝的行数(因为模板行本身占1行,所以-1)
270         int copyRows = dataRows - 1;
271         if (copyRows > 0) {
272             /**
273              * shiftRows: 从动态数据模版行(rowIndex)到最后一行,这些全部行都向下移copyRows行
274              *            相当于模版行上面插入n行空行(n=copyRows)
275              */
276             sheet.shiftRows(rowIndex, sheet.getLastRowNum(), copyRows, true, false);
277             // 拷贝策略
278             CellCopyPolicy cellCopyPolicy = makeCellCopyPolicy();
279             // 因为从模版行开始向下平移了copyRows行,所以这里 模板行=rowIndex + copyRows,
280             int templateDataRow = rowIndex + copyRows;
281             // 因为模版行上新增了空行,所以要把模板所在行的模版 拷贝到上面新增的空行
282             for (int i = 0; i < copyRows; i++) {
283                 //templateDataRow-模版行数据   rowIndex + i循环的当前空行
284                 sheet.copyRows(templateDataRow, templateDataRow, rowIndex + i, cellCopyPolicy);
285             }
286         }
287         // 循环模版行:动态替换模版行(将模版行里的模版替换成动态数据)
288         for (int j = rowIndex; j < rowIndex + dataRows; j++) {
289             Map<String, Object> dataMap = dynamicDataMapping.getDataList().get(j - rowIndex);
290             dealTemplateDataRow(sheet.getRow(j), dynamicDataMapping.getDataId(), dataMap);
291         }
292         return rowIndex + copyRows;
293     }
294 
295     /**
296      * @param
297      * @return org.apache.poi.ss.usermodel.CellCopyPolicy
298      * @description: 拷贝策略
299      */
300     public static CellCopyPolicy makeCellCopyPolicy() {
301         CellCopyPolicy cellCopyPolicy = new CellCopyPolicy();
302         cellCopyPolicy.setCopyCellValue(true);
303         cellCopyPolicy.setCopyCellStyle(true);
304         return cellCopyPolicy;
305     }
306 
307 
308 }


文章转载自:
http://dangerous .hfytgp.cn
http://salination .hfytgp.cn
http://heritor .hfytgp.cn
http://puttoo .hfytgp.cn
http://glacis .hfytgp.cn
http://retransfer .hfytgp.cn
http://recelebrate .hfytgp.cn
http://tortricid .hfytgp.cn
http://jibaro .hfytgp.cn
http://cardplaying .hfytgp.cn
http://xpvm .hfytgp.cn
http://fenfluramine .hfytgp.cn
http://sententia .hfytgp.cn
http://beadsman .hfytgp.cn
http://kilovolt .hfytgp.cn
http://laudably .hfytgp.cn
http://regardlessness .hfytgp.cn
http://magdalenian .hfytgp.cn
http://streaked .hfytgp.cn
http://maraca .hfytgp.cn
http://illumine .hfytgp.cn
http://upbind .hfytgp.cn
http://kdc .hfytgp.cn
http://scouter .hfytgp.cn
http://hesse .hfytgp.cn
http://sonatina .hfytgp.cn
http://gangbuster .hfytgp.cn
http://neurogenetics .hfytgp.cn
http://lixivia .hfytgp.cn
http://sha .hfytgp.cn
http://www.dtcms.com/a/293961.html

相关文章:

  • LoRA 低秩矩阵实现参数高效的权重更新
  • 2025 ACT 汽车功能安全相关PPT分享
  • 《计算机网络》实验报告七 HTTP协议分析与测量
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题汇总版,持续更新学习,加油!!!
  • 场效应管和模电的关于、学习
  • 【盘古100Pro+开发板实验例程】FPGA | Modelsim 的使用和 do 文件编写
  • SpringBoot 使用Rabbitmq
  • rust嵌入式开发零基础入门教程(一)
  • FSHNet:高效目标检测新突破
  • rust嵌入式开发零基础入门教程(五)
  • Python应用指南:构建和获取全球地铁线路数据及可视化
  • HarmonyOS学习记录5
  • 【形态学变换】——图像预处理(OpenCV)
  • (nice!!!)(LeetCode 每日一题) 1717. 删除子字符串的最大得分 (贪心)
  • 昨天去看了电科金仓的发布会,有点东西!
  • AI营销核心技术解析:运作机制与行业应用实例
  • 【软件系统架构】系列七:嵌入式系统性能深入解析
  • 华为云中,列表中的镜像无法删除可能由多种原因导致
  • 华为云开发者空间 × DeepSeek-R1 智能融合测评:云端开发与AI客服的协同进化
  • WPF的一些基础知识学习记录
  • 设计模式 八:原型模式 (Prototype Pattern)
  • Spring Boot全局异常处理:一网打尽Controller层异常,@RestControllerAdvice解析
  • 设计模式(单例)
  • 界面组件DevExpress WPF中文教程:Grid - 如何过滤节点?
  • Linux下SVN常用指令
  • 设计模式代码总结
  • Android MediaCodec 的使用和源码实现分析
  • 路由器与交换机的区别
  • 从入门到精通:Windows右键菜单管理全解析
  • 为什么 Linux 启动后还能升级内核?