农业集团信息管理系统富文本增强功能开发全记录
一、需求分析与技术选型
作为湖南某农业集团技术负责人,近期接到客户核心需求:在现有信息管理系统的后台文章发布模块中新增两大功能:
- 智能粘贴功能:支持从Word/微信公众号直接粘贴内容,图片自动上传至专用存储服务器(未来可平滑迁移至多云对象存储)
- 文档导入功能:支持Word/Excel/PPT/PDF全格式导入,完整保留样式与图片
技术栈现状:
- 前端:Vue2-CLI + UEditor(百度开源版)
- 后端:SpringBoot 2.7.x + Oracle 19c
- 部署环境:内网私有化部署(需支持离线环境)
- 存储方案:初期采用文件系统存储,预留对象存储接口
二、技术调研与方案评估
1. 富文本编辑器扩展方案
UEditor深度定制:
- 发现官方已停止维护,但社区活跃度较高
- 核心需求需解决:
- 保留Word样式(需解析Office Open XML)
- 图片二进制流处理(避免Base64污染DOM)
- 微信公众号特殊格式兼容
替代方案对比:
| 方案 | 优势 | 风险 |
|---|---|---|
| 扩展UEditor | 现有系统兼容性好,迁移成本低 | 需自行处理复杂文档解析 |
| 迁移wangEditor | 现代架构,支持插件机制 | 需重构现有编辑器集成 |
| 引入TinyMCE | 企业版支持Office文档 | 商业授权成本高 |
| 泽优WordPaster | 完全开源(下载源码) | |
| 支持任何系统 | ||
| 支持任何开发语言 | ||
| 支持主流编辑器 | ||
| 免费技术支持(QQ群:223813913) | 需要终端安全控件 |
最终决策:基于UEditor进行二次开发,通过插件机制实现功能扩展
2. 文档解析技术选型
Word/PPT解析:
- 采用Apache POI升级版(poi-tl 3.12+)
- 关键特性:
// 示例:提取Word中的图片与样式XWPFDocumentdoc=newXWPFDocument(inputStream);doc.getParagraphs().forEach(para->{// 提取段落样式CTRctr=para.getCTR();// 提取嵌入图片doc.getAllPictures().forEach(pic->{byte[]imageData=pic.getData();// 处理二进制流...});});
PDF解析:
- 选用PDFBox 2.0.27(Apache许可)
- 实现要点:
- 文本流提取时保留位置信息
- 图片提取需处理CCITT压缩格式
Excel解析:
- 使用EasyExcel 3.3.2(阿里开源)
- 特殊处理:
- 合并单元格样式映射
- 图表对象提取(暂不支持)
3. 存储架构设计
初期方案:
/storage /images /doc {docId}/ {md5}.jpg /temp对象存储接口:
publicinterfaceCloudStorageService{Stringupload(byte[]data,Stringext,Stringmodule);voiddelete(Stringkey);// 实现类包括AliyunOSSImpl, HuaweiOBSImpl等}三、核心功能实现
1. 智能粘贴功能开发
前端实现:
// UEditor插件扩展UE.plugins['smartpaste']=function(){constme=this;// 监听粘贴事件me.addListener('beforepaste',function(type,html){// 微信公众号内容特殊处理if(isWechatContent(html)){returnprocessWechatContent(html);}// Word内容处理constparser=newDOMParser();constdoc=parser.parseFromString(html,'text/html');constimages=doc.querySelectorAll('img[src^="blob:"]');// 批量上传图片returnPromise.all(Array.from(images).map(img=>{returnfetch(img.src).then(res=>res.blob()).then(blob=>uploadImage(blob));})).then(urls=>{// 替换图片URLleti=0;returnhtml.replace(/]+src="blob:[^"]+"/g,()=>{return`handleImage(@RequestParam("file")MultipartFile file,@RequestParam(value="module",defaultValue="doc")String module){// 生成唯一文件名String filename=UUID.randomUUID()+StringUtils.getExtension(file.getOriginalFilename());// 存储二进制数据String path=storageService.store(file.getBytes(),filename,module);returnResponseEntity.ok(newUploadResult(path));}}2. 文档导入功能实现
统一处理入口:
@ServicepublicclassDocumentImportService{publicImportResultimportDocument(MultipartFilefile)throwsIOException{Stringext=StringUtils.getExtension(file.getOriginalFilename());switch(ext.toLowerCase()){case"docx":case"doc":returnnewWordImporter().importDoc(file);case"xlsx":case"xls":returnnewExcelImporter().importXls(file);case"pptx":case"ppt":returnnewPptImporter().importPpt(file);case"pdf":returnnewPdfImporter().importPdf(file);default:thrownewIllegalArgumentException("不支持的文档类型");}}}Word导入核心逻辑:
publicclassWordImporter{publicImportResultimportDoc(MultipartFilefile)throwsIOException{try(XWPFDocumentdoc=newXWPFDocument(file.getInputStream())){ImportResultresult=newImportResult();// 处理段落doc.getParagraphs().forEach(para->{Stringtext=para.getText();if(StringUtils.isNotBlank(text)){result.addContent(text);}// 处理段落样式CTRctr=para.getCTR();if(ctr.isSetRPr()){CTRPrrpr=ctr.getRPr();// 解析字体、颜色等样式...}});// 处理图片(异步上传)Listpictures=doc.getAllPictures();pictures.forEach(pic->{Stringurl=uploadService.upload(pic.getData(),pic.suggestFileExtension(),"doc");result.addImage(url);});returnresult;}}}四、关键问题解决
1. 样式保留方案
字体处理:
- 建立字体映射表(解决Windows特有字体问题)
privatestaticfinalMapFONT_MAP=Map.of("Calibri","Arial","Microsoft YaHei","SimHei");颜色转换:
// 前端样式转换functionconvertColor(officeColor){// 处理Word中的RGB颜色(如#FF0000)if(/^#[0-9A-F]{6}$/i.test(officeColor)){returnofficeColor;}// 处理RGB值(如rgb(255,0,0))constmatch=officeColor.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);if(match){return`#${match.slice(1).map(c=>parseInt(c).toString(16).padStart(2,'0')).join('')}`;}return'#000000';}2. 性能优化措施
图片处理:
- 后端启用异步上传队列(RabbitMQ实现)
- 前端实现图片懒加载
大文件处理:
- 分片上传(基于WebUploader组件)
- 导入进度显示(WebSocket实时推送)
3. 安全加固方案
文件校验:
publicclassFileValidator{privatestaticfinalSetALLOWED_TYPES=Set.of("doc","docx","xls","xlsx","ppt","pptx","pdf");publicstaticvoidvalidate(MultipartFilefile){// 文件类型校验Stringext=StringUtils.getExtension(file.getOriginalFilename());if(!ALLOWED_TYPES.contains(ext.toLowerCase())){thrownewSecurityException("非法文件类型");}// 文件内容校验(魔数检测)byte[]header=newbyte[8];try(InputStreamis=file.getInputStream()){is.read(header);// 检测PDF魔数(%PDF-)if(ext.equals("pdf")&&!newString(header).startsWith("%PDF-")){thrownewSecurityException("无效的PDF文件");}// 其他格式检测...}catch(IOExceptione){thrownewRuntimeException("文件读取失败",e);}}}五、部署与测试
1. 私有化部署方案
Docker化部署:
FROM openjdk:8-jdk-alpine VOLUME /tmp ARG JAR_FILE=target/doc-manager-*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]Oracle配置:
# application.properties spring.datasource.url=jdbc:oracle:thin:@//192.168.1.100:1521/ORCL spring.datasource.username=DOC_MANAGER spring.datasource.password=ENC(加密密码) spring.datasource.driver-class-name=oracle.jdbc.OracleDriver2. 测试用例设计
核心场景测试:
| 测试类型 | 测试用例 | 预期结果 |
|---|---|---|
| 粘贴功能 | 从Word复制带图片的内容粘贴 | 图片正确上传,样式保留 |
| 导入功能 | 导入复杂格式的PPT | 幻灯片布局正确转换 |
| 性能测试 | 导入50MB的PDF文件 | 5分钟内完成,内存不溢出 |
| 安全测试 | 上传恶意脚本文件 | 系统拒绝并记录日志 |
六、后续优化方向
- 多云存储适配:开发阿里云/华为云等对象存储适配器
- 移动端适配:优化H5端的粘贴体验
- AI增强:集成OCR识别实现图片文字提取
- 版本控制:增加文档历史版本管理功能
整个开发周期历时6周,通过深度定制UEditor和精心设计后端处理流程,成功实现了客户要求的复杂文档处理功能。系统在内网环境稳定运行3个月后,已开始规划向公有云环境的迁移方案。
复制插件目录
引入插件文件
UEditor 1.4.3.3示例注意:不要重复引入jquery,如果您的项目已经引入了jq,则不用再引入jq-1.4
在工具栏中增加插件按钮
//工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的重新定义toolbars:[["fullscreen","source","|","zycapture","|","wordpaster","importwordtoimg","netpaster","wordimport","excelimport","pptimport","pdfimport","|","importword","exportword","importpdf"]]初始化控件
varpos=window.location.href.lastIndexOf("/");varapi=[window.location.href.substr(0,pos+1),"asp/upload.asp"].join("");WordPaster.getInstance({//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203edPostUrl:api,//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936ImageUrl:"",//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:"file",//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1ImageMatch:''});//加载控件注意
如果接口字段名称不是file,请配置FileFieldName。ueditor接口中使用的upfile字段
点击查看详细教程
配置ImageMatch
匹配图片地址,如果服务器返回的是JSON则需要通过正则匹配
ImageMatch:'',点击参考链接
配置ImageUrl
为图片地址增加域名,如果服务器返回的图片地址是相对路径,可通过此属性添加自定义域名。
ImageUrl:"",点击查看详细教程
配置SESSION
如果接口有权限验证(登陆验证,SESSION验证),请配置COOKIE。或取消权限验证。
参考:http://www.ncmem.com/doc/view.aspx?id=8602DDBF62374D189725BF17367125F3
效果
编辑器界面
导入Word文档,支持doc,docx
导入Excel文档,支持xls,xlsx
粘贴Word
一键粘贴Word内容,自动上传Word中的图片,保留文字样式。
Word转图片
一键导入Word文件,并将Word文件转换成图片上传到服务器中。
导入PDF
一键导入PDF文件,并将PDF转换成图片上传到服务器中。
导入PPT
一键导入PPT文件,并将PPT转换成图片上传到服务器中。
上传网络图片
下载示例
点击下载完整示例