枫丹的胡萝卜传说:芙宁娜与旅行者的奇幻旅程

核心内容摘要

台北娜娜身世之谜从台北街头到福建传闻,顶级尤物的真相究竟在哪_2
探索“八重神子精夜狂飙”:一次别开生面的漫画视觉盛宴

仙剑奇侠传3D精美大制作:免费动画,不容错过的奇幻盛宴!

一个Java程序员的UEditorWord导入血泪史从抓狂到真香

需求降临——老板的简单要求小张啊咱们后台编辑器得加个功能用户要能直接导入Word文档格式和图片都不能丢啊老板轻描淡写的一句话让我手里的咖啡差点喷到屏幕上。

作为一枚在Java后端摸爬滚打三年的程序员我深知这个简单需求背后的坑有多深。

但老板的微笑中带着不容置疑我只能默默打开IDEA开始了这场与Word格式的殊死搏斗。

前端探路——Vue2里的UEditor初体验

1 与UEditor的初次约会项目用的是vue2-cli我首先需要在前端集成UEditor。

网上搜了一圈发现官方有个vue-ueditor-wrap组件像是黑暗中的一盏明灯。

// main.js里引入importVueUEditorWrapfromvue-ueditor-wrapVue.component(vue-ueditor-wrap,VueUEditorWrap)// 组件中使用data(){return{editorConfig:{serverUrl:/api/ueditor/upload,// 后端接口UEDITOR_HOME_URL:/static/UEditor/// UEditor资源路径}}}

2 寻找Word导入插件UEditor官方没有Word导入功能我像只无头苍蝇在GitHub和Gitee上乱撞发现一个叫ueditor-word-import的插件但最后更新是3年前看到一个用Apache POI实现的方案但前端需要配合复杂终于在某个技术论坛找到线索——有个叫docx-converter的隐藏宝藏

后端攻坚——SpringBoot的文档处理大作战

1 文件上传接口初体验首先得实现UEditor的上传接口按照官方文档RestControllerRequestMapping(/api/ueditor)publicclassUEditorController{Value(${file.upload-dir})privateStringuploadDir;PostMapping(/upload)publicMapupload(RequestParam(upfile)MultipartFilefile){MapresultnewHashMap();try{//

确保目录存在FiledirnewFile(uploadDir);if(!dir.exists())dir.mkdirs();//

生成唯一文件名StringfileNameUUID.randomUUID()file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(.));//

保存文件file.transferTo(newFile(dir,fileName));//

返回UEditor需要的格式result.put(state,SUCCESS);result.put(url,/uploads/fileName);result.put(title,fileName);result.put(original,file.getOriginalFilename());}catch(IOExceptione){result.put(state,ERROR);}returnresult;}}

2 Word转HTML的终极方案经过多次尝试发现纯Java处理Word文档的几种方案Apache POI基础功能有但样式处理一塌糊涂docx4j功能强大但学习曲线陡峭Aspose.Words商业库效果最好但要钱JODConverter依赖OpenOffice/LibreOffice部署麻烦WordPaster国内唯一的商业化开源产品效果最好功能最强大最终选择了docx4j因为它纯Java实现对样式支持较好社区活跃ServicepublicclassWordConverterService{publicStringconvertDocxToHtml(MultipartFilefile)throwsException{//

加载Word文档WordprocessingMLPackagewordMLPackageWordprocessingMLPackage.load(file.getInputStream());//

配置HTML转换选项HTMLSettingshtmlSettingsDocx4J.createHTMLSettings();htmlSettings.setWmlPackage(wordMLPackage);//

自定义图片处理器htmlSettings.setImageHandler(newImageHandler(){OverridepublicStringhandleImage(WordprocessingMLPackagewordMLPackage,Partpart,StringrelationshipId){// 这里实现图片保存逻辑return/uploads/image-UUID.randomUUID().png;}});//

执行转换ByteArrayOutputStreamosnewByteArrayOutputStream();Docx4J.toHTML(htmlSettings,os,Docx4J.FLAG_EXPORT_PREFER_XSL);returnos.toString(UTF-

;}}

3 图片处理的血泪史Word里的图片是最头疼的部分我尝试了直接提取docx本质是zip可以解压获取图片但关联关系难处理内存中转换用docx4j的ImageHandler接口但需要自己实现存储临时文件方案// 在ImageHandler实现中OverridepublicStringhandleImage(WordprocessingMLPackagewordMLPackage,Partpart,StringrelationshipId){try{//

获取图片二进制数据InputStreamispart.getInputStream();//

保存到服务器StringfileNameimage-UUID.randomUUID().png;PathpathPaths.get(uploadDir,fileName);Files.copy(is,path,StandardCopyOption.REPLACE_EXISTING);//

返回可访问的URLreturn/uploads/fileName;}catch(IOExceptione){e.printStackTrace();return;}}

前后端联调——魔幻现实主义现场

1 前端调用后端接口在Vue组件中添加导入按钮methods:{importWord(){this.$refs.ueditor.editor.execCommand(insertHtml,正在导入Word...);// 实际项目中这里应该调用文件选择器// 然后通过FormData上传到后端转换接口// 模拟调用fetch(/api/word/convert,{method:POST,body:formData}).then(resres.text()).then(html{this.$refs.ueditor.editor.setContent(html);});}}

2 样式冲突大作战Word生成的HTML带有大量内联样式与UEditor默认样式冲突严重。

解决方案CSS重置/* 在UEditor的css中添加 */.word-import-content *{all:unset;/* 核武器级重置 */}.word-import-content p{margin:1em 0;/* 保留段落间距 */}选择性保留样式// 转换后处理HTMLfunctionsanitizeHtml(html){// 使用DOMParser解析constparsernewDOMParser();constdocparser.parseFromString(html,text/html);// 遍历所有元素保留需要的样式doc.querySelectorAll(*).forEach(el{// 只保留字体、颜色等基本样式conststyleswindow.getComputedStyle(el);constallowedStyles[font-family,color,font-size];allowedStyles.forEach(style{if(styles[style]!inherit){el.style[style]styles[style];}});// 移除其他样式el.removeAttribute(style);// 然后重新添加需要的样式...});returndoc.body.innerHTML;}

数据库设计——给HTML找个家

1 简单方案CREATETABLEarticle(idBIGINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(

NOTNULL,contentTEXTNOTNULL,-- 直接存HTMLcreate_timeDATETIMEDEFAULTCURRENT_TIMESTAMP);

2 高级方案带图片管理CREATETABLEarticle(idBIGINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(

NOTNULL,contentTEXTNOTNULL,html_pathVARCHAR(

,-- 大内容存文件路径word_source_pathVARCHAR(

,-- 原始Word路径create_timeDATETIMEDEFAULTCURRENT_TIMESTAMP);CREATETABLEarticle_image(idBIGINTAUTO_INCREMENTPRIMARYKEY,article_idBIGINTNOTNULL,image_urlVARCHAR(

NOTNULL,alt_textVARCHAR(

,sort_orderINTDEFAULT0,FOREIGNKEY(article_id)REFERENCESarticle(id));

最终胜利与经验宝典经过两周的奋战项目终于上线。

现在回想起来关键点有技术选型前端vue-ueditor-wrap 自定义按钮后端SpringBoot docx4j存储MySQL TEXT字段 文件系统避坑指南不要试图完美还原Word所有样式图片处理要尽早考虑存储方案转换后的HTML一定要做安全过滤性能优化大文件分块上传异步处理转换任务使用缓存避免重复转换最后附上完整技术栈前端Vue2 vue-ueditor-wrap后端SpringBoot

7 docx4j

3数据库MySQL

0构建工具Maven webpack现在当看到用户顺利导入Word文档格式和图片都完美保留时那种成就感就像征服了珠穆朗玛峰——虽然过程艰辛但风景独好复制插件目录引入插件文件UEditor

1.

4.

3示例注意不要重复引入jquery如果您的项目已经引入了jq则不用再引入jq-

4在工具栏中增加插件按钮//工具栏上的所有的功能按钮和下拉框可以在new编辑器的实例时选择自己需要的重新定义toolbars:[[fullscreen,source,|,zycapture,|,wordpaster,importwordtoimg,netpaster,wordimport,excelimport,pptimport,pdfimport,|,importword,exportword,importpdf]]初始化控件varposwindow.location.href.lastIndexOf(/);varapi[window.location.href.substr(0,pos

,asp/upload.asp].join();WordPaster.getInstance({//上传接口http://www.ncmem.com/doc/view.aspx?idd88b60a2b0204af1ba62fa66288203edPostUrl:api,//为图片地址增加域名http://www.ncmem.com/doc/view.aspx?id704cd302ebd346b486adf39cf4553936ImageUrl:,//设置文件字段名称http://www.ncmem.com/doc/view.aspx?idc3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:file,//提取图片地址http://www.ncmem.com/doc/view.aspx?id07e3f323d22d4571ad213441ab8530d1ImageMatch:});//加载控件注意如果接口字段名称不是file请配置FileFieldName。

ueditor接口中使用的upfile字段点击查看详细教程配置ImageMatch匹配图片地址如果服务器返回的是JSON则需要通过正则匹配ImageMatch:,点击参考链接配置ImageUrl为图片地址增加域名如果服务器返回的图片地址是相对路径可通过此属性添加自定义域名。

ImageUrl:,点击查看详细教程配置SESSION如果接口有权限验证登陆验证SESSION验证请配置COOKIE。

或取消权限验证。

参考http://www.ncmem.com/doc/view.aspx?id8602DDBF62374D189725BF17367125F3粘贴效果导入效果下载示例点击下载完整示例

夜月APP直播YY22-夜月APP直播应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123