日期:2023年X月X日
角色:北京XX软件公司 全栈开发工程师
项目背景:客户要求在后台管理系统的文章发布模块新增三大功能:
- Word粘贴功能:支持从Word复制内容粘贴到UEditor,图片自动上传至内网单据存储服务器(未来可迁移至阿里云/华为云等对象存储),保留样式(字体、字号、颜色等)。
- 文档导入功能:支持上传Word/Excel/PPT/PDF,解析内容并保留图片和样式,渲染至UEditor。
- 微信公众号内容粘贴:支持从微信公众号编辑器复制内容(含富文本和图片)并粘贴到UEditor,图片自动上传至内网服务器。
技术栈:
- 前端:Vue2-cli + UEditor(富文本编辑器)
- 后端:SpringBoot(Java)
- 数据库:Oracle(存储文章元数据,图片路径存储在文件系统或对象存储)
- 服务器:内网私有部署(未来支持多云对象存储)
一、技术可行性分析与工具选型
1. Word粘贴功能
核心问题:
- 浏览器原生粘贴事件无法直接处理Word中的复杂样式(如
mso-前缀的CSS)和图片(图片以base64或剪贴板对象形式存在)。 - UEditor默认会过滤Word特有的HTML标签和样式,需自定义过滤规则。
- 图片需从
base64转换为文件并上传至内网服务器,生成可访问的URL。
解决方案:
- 前端:
- 拦截粘贴事件,提取HTML和图片
base64数据。 - 使用
axios将图片上传至后端,替换HTML中的base64为服务器URL。 - 调整UEditor配置,保留Word相关CSS属性(如
font-family、color)。
- 拦截粘贴事件,提取HTML和图片
- 后端(SpringBoot):
- 提供图片上传接口,将图片保存至内网服务器,返回访问路径。
- 支持未来迁移至对象存储(如阿里云OSS),通过配置切换存储服务。
工具评估:
mammoth.js:轻量级Word转HTML库,但无法处理图片和复杂样式。docx-preview:纯前端解析Word,但与UEditor集成复杂。- 自定义方案:基于UEditor的
afterPaste钩子,结合SpringBoot图片上传接口实现。
结论:采用自定义方案,前端处理粘贴事件,后端提供图片上传服务。
2. 文档导入功能
核心问题:
- 需支持多种格式(Word/Excel/PPT/PDF),并保留样式和图片。
- 大文件(如PPT)解析性能优化,避免阻塞主线程。
- 图片需从文档中提取并上传至服务器。
解决方案:
- 后端(SpringBoot):
- Word/Excel/PPT:使用
Apache POI或Aspose(商业库)解析文档,提取文本、图片和样式。 - PDF:使用
Apache PDFBox或iText解析文本,图片需额外处理。 - 图片上传至内网服务器,生成URL替换文档中的本地路径。
- Word/Excel/PPT:使用
- 前端:
- 通过文件上传组件(如
el-upload)将文档发送至后端。 - 后端返回解析后的HTML,注入UEditor。
- 通过文件上传组件(如
工具评估:
Apache POI:开源免费,但解析复杂格式(如PPT)需额外开发。Aspose:功能强大,支持所有格式,但需商业授权。PDFBox:开源PDF解析库,但样式保留能力有限。
结论:
- Word/Excel:使用
Apache POI(开源优先)。 - PPT/PDF:评估
Aspose试用版或结合LibreOffice命令行转换(需服务器安装)。
3. 微信公众号内容粘贴
核心问题:
- 微信公众号编辑器导出的HTML可能包含微信特有的CSS类名(如
wx_fmt_*)。 - 图片以微信CDN链接形式存在,需下载并上传至内网服务器。
解决方案:
- 前端:
- 拦截粘贴事件,提取HTML中的微信图片URL。
- 通过后端接口下载图片并上传至内网服务器,替换HTML中的CDN链接。
- 后端:
- 提供图片下载接口,支持从微信CDN拉取图片并保存至本地。
工具评估:
Jsoup(Java):解析HTML并提取图片URL。HttpClient(SpringBoot):下载微信图片。
结论:自定义实现,前端处理HTML替换,后端提供下载服务。
二、开发过程记录
1. Word粘贴功能实现
前端(Vue2 + UEditor):
// 监听UEditor粘贴事件consteditor=UE.getEditor('editor');editor.addListener('afterPaste',function(){consthtml=editor.getContent();if(html.includes('mso-')||html.includes('data:image')){handleWordPaste(html);}});asyncfunctionhandleWordPaste(html){// 提取base64图片并上传constimgTags=html.match(/]+src="data:image[^>]+>/g)||[];for(constimgTagofimgTags){constbase64=imgTag.match(/src="data:image([^;]+);base64,([^"]+)"/);if(base64){constresponse=awaituploadToServer(base64[2],base64[1]);// 调用后端接口constnewImgTag=imgTag.replace(base64[0],`src="${response.url}"`);html=html.replace(imgTag,newImgTag);}}editor.setContent(html);// 更新编辑器内容}后端(SpringBoot):
@RestController@RequestMapping("/api/upload")publicclassUploadController{@Value("${file.upload-dir}")// 内网服务器路径privateStringuploadDir;@PostMapping("/word-image")publicResponseEntity>uploadWordImage(@RequestParam("image")Stringbase64,@RequestParam("type")Stringtype){StringfileName=UUID.randomUUID()+"."+type;StringfilePath=uploadDir+"/"+fileName;// 解码base64并写入文件byte[]decodedBytes=Base64.getDecoder().decode(base64);Files.write(Paths.get(filePath),decodedBytes);// 返回内网可访问的URL(需配置Nginx反向代理)Stringurl="http://内网服务器地址/uploads/"+fileName;returnResponseEntity.ok(Map.of("url",url));}}2. 文档导入功能实现
后端(SpringBoot + Apache POI):
// Word导入示例(DOCX)@PostMapping("/import/docx")publicResponseEntityimportDocx(@RequestParam("file")MultipartFilefile)throwsIOException{XWPFDocumentdocument=newXWPFDocument(file.getInputStream());StringBuilderhtml=newStringBuilder();for(XWPFParagraphparagraph:document.getParagraphs()){html.append("");for(XWPFRunrun:paragraph.getRuns()){html.append("").append(run.getText(0)).append("");}html.append("");}// 提取图片并上传(简化示例)for(XWPFPictureDatapicture:document.getAllPictures()){byte[]bytes=picture.getData();// 上传至服务器并替换HTML中的图片路径}returnResponseEntity.ok(html.toString());}前端(Vue2文件上传):
export default { methods: { handleImportSuccess(response) { const editor = UE.getEditor('editor'); editor.setContent(response); // 注入解析后的HTML } } };3. 微信公众号内容粘贴实现
前端(Vue2):
asyncfunctionhandleWechatPaste(html){// 提取微信图片URL(示例:src="https://mmbiz.qpic.cn/...")constimgTags=html.match(/]+src="https:\/\/mmbiz\.qpic\.cn[^>]+>/g)||[];for(constimgTagofimgTags){consturl=imgTag.match(/src="([^"]+)"/)[1];constresponse=awaitdownloadFromWechat(url);// 调用后端接口constnewImgTag=imgTag.replace(url,response.url);html=html.replace(imgTag,newImgTag);}consteditor=UE.getEditor('editor');editor.setContent(html);}后端(SpringBoot):
@PostMapping("/download/wechat-image")publicResponseEntity>downloadWechatImage(@RequestParam("url")Stringurl)throwsIOException{// 使用HttpClient下载图片HttpEntityentity=newHttpEntity<>(headers);ResponseEntityresponse=restTemplate.exchange(url,HttpMethod.GET,entity,byte[].class);// 保存至内网服务器StringfileName=UUID.randomUUID()+".jpg";StringfilePath=uploadDir+"/"+fileName;Files.write(Paths.get(filePath),response.getBody());// 返回内网URLStringaccessibleUrl="http://内网服务器地址/uploads/"+fileName;returnResponseEntity.ok(Map.of("url",accessibleUrl));}三、问题与优化
- 性能问题:
- 大文件解析(如PPT)采用异步任务队列(如RabbitMQ)优化。
- 样式兼容性:
- Word中的复杂样式(如文本框)需手动转换为HTML/CSS。
- 安全性:
- 对上传文件进行MIME类型校验,防止恶意文件上传。
- 多云支持:
- 封装存储服务接口(如
StorageService),未来可替换为阿里云/华为云实现。
- 封装存储服务接口(如
四、总结
通过结合UEditor的扩展能力、SpringBoot的文件处理能力和Apache POI的文档解析能力,成功实现了客户需求。后续计划将文档解析服务拆分为独立微服务,并完善多云对象存储的适配层。
复制插件目录
引入插件文件
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转换成图片上传到服务器中。
上传网络图片
下载示例
点击下载完整示例