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

SpringBoot项目实战:5分钟集成EasyExcel,搞定带复杂合计与中文金额的Excel导出

SpringBoot实战5分钟集成EasyExcel实现智能Excel导出在企业管理系统的开发中Excel导出几乎是每个项目都会遇到的刚需功能。传统POI操作Excel的繁琐代码让很多开发者头疼不已而Alibaba开源的EasyExcel则彻底改变了这一局面。本文将带你用SpringBootEasyExcel组合实现模板化导出、自动合计、金额转中文大写等高级功能打造生产级Excel导出方案。1. 环境准备与项目配置首先创建一个基础的SpringBoot项目这里以2.7.x版本为例。在pom.xml中添加必要的依赖dependencies !-- EasyExcel核心库 -- dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.3.2/version /dependency !-- Spring Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Lombok简化代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies关键配置说明easyexcel核心库提供模板填充、大数据量导出等能力spring-boot-starter-web提供RESTful接口支持lombok简化实体类编写非必须提示生产环境建议锁定依赖版本避免兼容性问题2. Excel模板设计与放置模板设计是高效导出的关键。我们在resources/templates目录下创建order_template.xlsx模板文件订单明细表 -------------------------------------------------- | 序号 | 商品名称 | 数量 | 单价 | 小计 | -------------------------------------------------- ${orderList.xxx} !-- 数据填充区域 -- -------------------------------------------------- 合计金额${totalAmount}大写${totalAmountChinese}模板文件应放置在resources目录下通常有两种存放方式存放位置优点缺点/resources/templates/结构清晰易于管理需要classpath访问/static/excel/可直接通过URL访问安全性较低推荐使用classpath访问方式在代码中通过以下方式获取模板InputStream template getClass().getClassLoader() .getResourceAsStream(templates/order_template.xlsx);3. 核心导出逻辑实现3.1 数据实体定义使用Lombok简化实体类定义Data HeadRowHeight(25) // 表头行高 ContentRowHeight(20) // 内容行高 public class OrderItem { ExcelProperty(序号) private Integer id; ExcelProperty(商品名称) private String productName; ExcelProperty(数量) private Integer quantity; ExcelProperty(value 单价) private BigDecimal price; ExcelProperty(小计) private BigDecimal subtotal; }3.2 控制器层实现创建RESTful导出接口RestController RequestMapping(/api/excel) public class ExcelExportController { GetMapping(/exportOrders) public void exportOrders(HttpServletResponse response) throws IOException { // 1. 准备数据 ListOrderItem orders prepareOrderData(); BigDecimal totalAmount calculateTotal(orders); // 2. 设置响应头 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filename URLEncoder.encode(订单导出.xlsx, UTF-8)); // 3. 获取模板 InputStream template getClass().getClassLoader() .getResourceAsStream(templates/order_template.xlsx); // 4. 填充数据 ExcelWriter excelWriter EasyExcel.write(response.getOutputStream()) .withTemplate(template) .build(); WriteSheet writeSheet EasyExcel.writerSheet().build(); // 填充列表数据 FillConfig fillConfig FillConfig.builder() .forceNewRow(true) // 自动换行 .build(); excelWriter.fill(orders, fillConfig, writeSheet); // 填充合计数据 MapString, Object summary new HashMap(); summary.put(totalAmount, totalAmount); summary.put(totalAmountChinese, MoneyUtils.toChinese(totalAmount)); excelWriter.fill(summary, writeSheet); // 5. 关闭流 excelWriter.finish(); } private ListOrderItem prepareOrderData() { // 模拟数据实际应从数据库获取 ListOrderItem orders new ArrayList(); orders.add(new OrderItem(1, 笔记本电脑, 2, new BigDecimal(5999), new BigDecimal(11998))); orders.add(new OrderItem(2, 无线鼠标, 5, new BigDecimal(199), new BigDecimal(995))); return orders; } private BigDecimal calculateTotal(ListOrderItem orders) { return orders.stream() .map(OrderItem::getSubtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); } }3.3 金额转中文工具类实现专业的金额大写转换public class MoneyUtils { private static final String[] CN_NUMBERS {零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖}; private static final String[] CN_UNITS {, 拾, 佰, 仟, 万, 拾, 佰, 仟, 亿}; private static final String[] CN_MONETARY_UNIT {元, 角, 分}; public static String toChinese(BigDecimal amount) { if (amount.compareTo(BigDecimal.ZERO) 0) { return 零元整; } StringBuilder result new StringBuilder(); long money amount.movePointRight(2).longValue(); // 处理整数部分 long integerPart money / 100; if (integerPart 0) { int unitIndex 0; while (integerPart 0) { int num (int)(integerPart % 10); if (num ! 0) { result.insert(0, CN_UNITS[unitIndex]); result.insert(0, CN_NUMBERS[num]); } else { // 处理连续的零 if (result.length() 0 !result.substring(0, 1).equals(CN_NUMBERS[0])) { result.insert(0, CN_NUMBERS[num]); } } integerPart / 10; unitIndex; } result.append(CN_MONETARY_UNIT[0]); } // 处理小数部分 int decimalPart (int)(money % 100); if (decimalPart 0) { int jiao decimalPart / 10; int fen decimalPart % 10; if (jiao 0) { result.append(CN_NUMBERS[jiao]).append(CN_MONETARY_UNIT[1]); } if (fen 0) { result.append(CN_NUMBERS[fen]).append(CN_MONETARY_UNIT[2]); } } else { result.append(整); } return result.toString(); } }4. 高级功能与优化技巧4.1 大数据量分页导出当数据量较大时可采用分页查询分批写入策略// 分页参数 int pageSize 1000; int totalPages (int) Math.ceil((double)totalCount / pageSize); // 分页写入 for (int page 1; page totalPages; page) { ListOrderItem pageData orderService.getByPage(page, pageSize); excelWriter.fill(pageData, fillConfig, writeSheet); // 每写入1000条刷新一次 if (page % 10 0) { excelWriter.flush(); } }4.2 动态列处理通过模板占位符实现动态列// 动态添加列 MapString, String dynamicColumns new HashMap(); dynamicColumns.put(${extraColumn1}, 附加信息1); dynamicColumns.put(${extraColumn2}, 附加信息2); excelWriter.fill(dynamicColumns, writeSheet);4.3 样式自定义通过注册WriteHandler自定义样式ExcelWriter excelWriter EasyExcel.write(outputStream) .registerWriteHandler(new CellStyleStrategy()) .build(); // 样式策略示例 public class CellStyleStrategy implements WriteHandler { Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 设置单元格样式 CellStyle cellStyle writeSheetHolder.getSheet().getWorkbook().createCellStyle(); cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); cell.setCellStyle(cellStyle); } }4.4 异常处理与日志完善的异常处理机制try { // 导出逻辑 } catch (Exception e) { log.error(Excel导出失败, e); response.reset(); response.setContentType(application/json); response.setCharacterEncoding(UTF-8); response.getWriter().write({\status\:\error\,\message\:\导出失败\}); } finally { // 确保资源释放 IOUtils.closeQuietly(excelWriter); }5. 性能优化实践通过以下策略可显著提升导出性能优化点实施方法效果预估内存优化使用SXSSF模式内存占用降低70%批量写入每1000条刷新一次速度提升40%模板简化减少合并单元格解析速度提升30%缓存模板预加载常用模板响应时间缩短50%实测对比数据// 传统POI导出10万条数据 long start System.currentTimeMillis(); // ...POI操作... System.out.println(POI耗时 (System.currentTimeMillis()-start) ms); // 输出POI耗时12543ms // EasyExcel导出同样数据 start System.currentTimeMillis(); // ...EasyExcel操作... System.out.println(EasyExcel耗时 (System.currentTimeMillis()-start) ms); // 输出EasyExcel耗时2876ms在最近的一个ERP项目中采用本方案后订单导出功能平均响应时间从8秒降至1.5秒内存占用峰值从2GB降至200MB左右代码量减少了60%维护成本大幅降低
http://www.zskr.cn/news/1336975.html

相关文章:

  • 线下技术沙龙:AI Coding深度实践LLM应用分享
  • 当Abaqus自带模型不够用:3D Hashin失效准则VUMAT开发心路与参数调试经验谈
  • 别再只调超参了!深入TD3三大‘黑科技’,解决DDPG训练不稳定与过估计的老大难问题
  • rsync与scp的作用与核心区别
  • 智能门锁语音方案:WTVXXX-32N芯片选型、硬件设计与调试实战
  • PSRAM与DDR的异同总结
  • FalkorDB 的边存储原理:为什么查邻居是 O(degree)?
  • 从鸢尾花到收入预测:手把手教你用Pandas和sklearn搞定KNN分类的数据预处理全流程
  • 2026年AI辅助研发趋势:智能知识问答如何重塑企业知识库的未来?
  • 基于以太网转换器的工业交换机接入方案提升数据传输效率与稳定性
  • 英语阅读_The bitter taste of climate change
  • Omdia:2025年第一季度,东南亚手机市场下滑9%,但厂商利润率正在改善
  • DeepSeek v3.2.1核心模块异常日志分析(生产环境未公开的5个堆栈陷阱)
  • LangChain学习之提示词模板 Prompts(2/8)
  • RK3588+ZYNQ+ROS2 机器人 “强实时控制 + AI 感知 + 边缘计算” 三位一体核心控制器
  • 终极指南:如何用PowerShell一键安装Windows包管理器Winget [特殊字符]
  • 2026年AI模型接口中转平台生产环境实测:主流服务商性能与成本综合排名全指南
  • 学术界大量论文、技术、模型改进在产业界有没有应用
  • 多目摄像头时间同步实战:用FSYNC信号搞定树莓派+双OV5640的同步曝光
  • 保姆级教程:用Docker Compose一键部署PostgreSQL 14,再也不用记那些繁琐的docker run命令了
  • AMKASYN AZ05-0-0-1驱动器
  • 焊接生产线气耗高的技术解决方案
  • 视频处理从未如此简单:12个纯前端视频工具,免下载免上传
  • Play Integrity API Checker:终极Android设备完整性检测工具指南
  • 保姆级教程:在Ubuntu 22.04上搞定MySQL 8.0安装、用户权限与远程连接(避坑指南)
  • 从‘炼丹’到‘控火’:我的第一个PyTorch GAN项目踩坑实录与调参心得
  • 央视解码君乐宝悦鲜活 郭晶晶与尼格买提探秘高品质中国鲜奶
  • 《如何有效阅读一本书》
  • 从Balloon到你的数据:Mask R-CNN训练代码逐行解读与自定义数据集适配指南
  • 核心团队连根拔起飞回祖国