尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Excel 批量导入实战:当 EasyExcel 遇上单元格嵌入附件

Excel 批量导入实战:当 EasyExcel 遇上单元格嵌入附件
📅 发布时间:2026/6/23 20:11:59

Excel 批量导入实战:当 EasyExcel 遇上单元格嵌入附件

适用场景:后端需要解析用户上传的 Excel,其中不仅包含常规文本数据,还包含直接嵌入单元格的附件(zip、docx、图片等),要求一次性完成数据入库、附件落盘、关联业务实体。


一、场景与痛点

在常见的 B 端后台中,运营/产品同学往往通过一份 Excel 模板批量录入业务数据。模板里除了常规字段,还经常出现“需求附件”列——用户把文件直接拖进 Excel 单元格里。这对后端提出了两个核心挑战:

  1. MultipartFile 的流只能读一次:EasyExcel 读完之后,如果再用 POI 读,流已经耗尽。
  2. Excel 里的附件不是超链接,而是二进制对象:需要精确解析 OLE、OOXML、图片三种嵌入形态,并按行号对应回业务数据。
  3. Excel中的附件类型,名称获取问题:excel对实体附件的数据和名称存储在不同的位置,需要根据特定的格式进行解析,且部分格式如:.xlsx和.docx格式的名称并无存储,本文暂时只找到对.zip的名称支持。

本文以“设计需求批量导入”模块为实例,给出一条可复用的工程化路径。


二、技术栈选型

组件用途版本参考
EasyExcel快速读取文本数据、类型转换、注解映射2.x / 3.x
Apache POI (XSSF)解析底层 OOXML 结构,提取单元格嵌入对象5.x
Hutool字符串、集合、空值工具5.x
Spring Boot事务控制、Service 层编排3.x
自定义 OSS 工具附件上传、路径管理—

为什么不只用 EasyExcel?
EasyExcel 专注于高性能的文本/数值读取,对单元格内嵌 OLE/图片对象只提供有限支持。对于“需要把附件捞出来并上传 OSS”的场景,必须下沉到 Apache POI 的XSSFWorkbook+XSSFDrawing层做原生解析。


三、整体架构流程

用户上传 Excel (.xlsx) │ ▼ ┌───────────────────┐ │ 1. 读取字节数组 │ ← 关键:先复制 bytes,解决流不可重复读 │ byte[] fileBytes │ └───────────────────┘ │ ┌───┴───┐ ▼ ▼ EasyExcel Apache POI (文本数据) (嵌入附件) │ │ ▼ ▼ List<VO> List<EmbeddingDesc> │ │ └───┬───┘ ▼ ┌───────────────────┐ │ 2. 按行号关联 │ ← 把附件挂到对应 VO 上 │ vo.setAttachments()│ └───────────────────┘ │ ▼ ┌───────────────────┐ │ 3. 逐行校验 │ ← 字典、用户、日期、多选字段、岗位-类型联动 │ validate/convert │ └───────────────────┘ │ ▼ ┌───────────────────┐ │ 4. 分批收集 │ ← 主表PO、用户绑定、Code映射、附件信息 │ 4 个临时集合 │ └───────────────────┘ │ ▼ ┌───────────────────┐ │ 5. 批量写入 │ ← 先插主表,再插关联表,最后上传附件 │ insert + OSS upload│ └───────────────────┘ │ ▼ ┌───────────────────┐ │ 6. 后置业务 │ ← 操作历史、审批流、待办、消息通知 └───────────────────┘

四、关键实现拆解

4.1 文件字节复制:解决流不可重复读

MultipartFile.getInputStream()只能消费一次。EasyExcel 和 POI 各自需要独立流,因此第一步就是把文件读成字节数组。

byte[]fileBytes=file.getBytes();// 1. EasyExcel 读取List<DesignDemandImportVo>importVos=EasyExcel.read(newByteArrayInputStream(fileBytes)).head(DesignDemandImportVo.class).sheet("DesignDemand").doReadSync();// 2. POI 解析附件(同样基于 fileBytes)List<ExcelAttachmentParser.EmbeddingDesc>embeddings=ExcelAttachmentParser.resolve(fileBytes);

通用经验:凡是要用两套工具解析同一个上传文件,先读 bytes,再各自 new ByteArrayInputStream,不要直接传file.getInputStream()。


4.2 双管道解析:文本 + 附件

文本管道:EasyExcel
@DatapublicclassDesignDemandImportVo{@ExcelProperty("品类")privateStringcategoryType_dictText;@ExcelProperty("希望交付时间")privateStringhopeDeliveryDate;@ExcelProperty("需求附件")privateStringattachmentZip;// 纯占位,不需要填内容@ExcelIgnoreprivateList<CellAttachment>attachments=newArrayList<>();// 真正存附件数据}
  • 用@ExcelProperty做列名映射;@ExcelIgnore字段不映射列,由代码手动填充。
  • 日期在 Excel 中可能是文本格式,所以先按String接收,再手动解析为LocalDate,避免格式错乱。
附件管道:Apache POI 原生解析

ExcelAttachmentParser.resolve(byte[] xlsxBytes)是核心工具类。它遍历XSSFDrawing,识别三种嵌入形态:

  1. OLE 对象:zip、docx、xlsx 等,通常以\u0001Ole10Native或package条目存储在POIFSFileSystem中。
  2. OOXML 原生嵌入:无 OLE2 包装的直接嵌入,直接读PackagePart的流。
  3. 粘贴图片:XSSFPicture,通过XSSFPictureData获取 PNG/JPEG 字节。
publicstaticList<EmbeddingDesc>resolve(byte[]xlsxBytes){try(XSSFWorkbookworkbook=newXSSFWorkbook(newByteArrayInputStream(xlsxBytes))){for(inti=0;i<workbook.getNumberOfSheets();i++){XSSFSheetsheet=workbook.getSheetAt(i);XSSFDrawingdrawing=sheet.getDrawingPatriarch();if(drawing==null)continue;for(XSSFShapeshape:drawing.getShapes()){XSSFClientAnchoranchor=(XSSFClientAnchor)shape.getAnchor();introw=anchor.getRow1();// 获取附件所在行号if(shapeinstanceofXSSFObjectData){// OLE / OOXML 嵌入对象...}elseif(shapeinstanceofXSSFPicture){// 图片...}}}}}

OLE 文件名乱码是重灾区。Windows 中文系统默认用 GBK 编码文件名,但 Office 2016+ 可能用 UTF-8。解析器做了多编码尝试 + 启发式乱码检测:

  • 优先 GBK → GB18030 → UTF-8 → 兜底 ISO-8859-1
  • 通过looksLikeMojibake()检测典型乱码特征(Â、Ã、¿等),反向选择正确编码。

下面是ExcelAttachmentParser完整代码

importlombok.extern.slf4j.Slf4j;importorg.apache.commons.io.IOUtils;importorg.apache.poi.openxml4j.opc.PackagePart;importorg.apache.poi.poifs.filesystem.*;importorg.apache.poi.xssf.usermodel.*;importjava.io.ByteArrayInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.nio.ByteBuffer;importjava.nio.ByteOrder;importjava.nio.charset.StandardCharsets;importjava.util.*;/** * Excel单元格嵌入附件解析器 */@Slf4jpublicclassExcelAttachmentParser{publicstaticList<EmbeddingDesc>resolve(byte[]xlsxBytes){List<EmbeddingDesc>result=newArrayList<>();try(XSSFWorkbookworkbook=newXSSFWorkbook(newByteArrayInputStream(xlsxBytes))){// 获取所有嵌入式文件for(inti=0;i<workbook.getNumberOfSheets();i++){XSSFSheetsheet=workbook.getSheetAt(i);XSSFDrawingdrawing=sheet.getDrawingPatriarch();if(drawing==null)continue

相关新闻

  • 终极免费方案:如何让小爱音箱告别会员限制,实现无限音乐自由
  • 自然语言驱动全栈开发:从想法到完整项目,AI 编程的能力边界在哪里
  • 如何用猫抓Cat-Catch实现浏览器资源嗅探:终极免费视频下载工具指南

最新新闻

  • 基于TestHub的接口自动化测试框架:从分层设计到CI/CD集成实战
  • 3步掌握碧蓝航线自动化:Alas智能助手解放你的游戏时间
  • Android逆向实战:Hook dlopen绕过Frida检测机制
  • 企业信息化升级,OA系统助力高效办公
  • 华玺AI观察:华玺云科认为,全球市场智能体不是多语种翻译工具
  • 如何用XUnity.AutoTranslator为Unity游戏实现高效自动化翻译

日新闻

  • Arduino-ESP32项目深度解析:解锁隐藏芯片支持与架构演进
  • 2026年 系统窗厂家/品牌推荐榜单:隔音系统窗+高端系统门窗的核心优势与选购指南 - 品牌发掘
  • NVBench:首个双语非言语发声语音合成评测基准详解与实践

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号