news 2026/6/15 17:22:09

医院HIS系统如何解决wangEditor粘贴Word表格变形?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医院HIS系统如何解决wangEditor粘贴Word表格变形?

《苏州程序员の暑假奇遇记:Word图片转存大作战》
日期:2023年7月15日 星期五 苏州 暴雨转晴(适合宅家敲代码)

第一章:需求诞生——从“懒癌晚期”到“技术狂魔”

“客户爸爸说,他们每天要从Word里抠100+张图片存到服务器,手动右键另存为+FTP上传,手指都快磨出老茧了!”
我叼着冰棍瘫在电竞椅上,看着屏幕里客户发来的“血泪控诉”,突然觉得自己的暑假不该只用来肝《原神》。
“这需求,不就是‘Word图片一键转存+自动上传服务器’吗?简单!”(Flag立得飞起)


第二章:组件狩猎——在GitHub的海洋里“捞针”

打开GitHub,输入关键词“word image extract”,结果如下:

  1. mammoth.js:能转Word为HTML,但图片是base64编码的,上传得自己处理(“这不就是让我手动搓轮子吗?”)
  2. docx-preview:能预览Word,但图片提取?“抱歉,本库只负责看,不负责摸。”
  3. officegen:生成Word的神器,但反向操作?“反向抽烟?不会!”

绝望之际,突然想到——
“既然Word能转HTML,那图片肯定是``标签啊!直接抓src里的base64,转Blob上传不就行了?”
(拍桌)“我真是个天才!现在只需要找个能解析Word的库!”


第三章:前端攻坚——Vue2 + wangEditor5的“梦幻联动”

Step 1:让wangEditor5支持Word粘贴

wangEditor5默认不支持直接粘贴Word内容,但官方文档说可以“自定义扩展”。
代码片段

// src/utils/editorConfig.jsimport{Editor}from'wangeditor5'import{Boot}from'@wangeditor5/core'// 自定义粘贴过滤器Boot.registerModule({name:'pasteFilter',editor:Editor,config:(editor)=>{editor.on('paste',(data)=>{if(data.type==='text/html'){consthtml=data.data// 简单处理:提取所有标签(实际需更复杂的DOM解析)constimgTags=html.match(/]+src="([^">]+)"/g)||[]console.log('提取到的图片:',imgTags)}})}})

测试结果

  • 复制Word图片→粘贴到编辑器→控制台输出``标签,但srcdata:image/png;base64,...(成功一半!)
Step 2:Base64转Blob并上传

代码片段

// src/utils/imageUploader.jsexportasyncfunctionuploadWordImages(base64List){constformData=newFormData()base64List.forEach((base64,index)=>{// Base64转Blobconstblob=dataURLtoBlob(base64)formData.append(`images[]`,blob,`word_image_${index}.png`)})// 调用阿里云OSS直传(后端签名的URL)constresponse=awaitfetch('https://your-server.com/api/upload-sign',{method:'POST',body:formData})returnresponse.json()}functiondataURLtoBlob(dataurl){constarr=dataurl.split(',')constmime=arr[0].match(/:(.*?);/)[1]constbstr=atob(arr[1])letn=bstr.lengthconstu8arr=newUint8Array(n)while(n--){u8arr[n]=bstr.charCodeAt(n)}returnnewBlob([u8arr],{type:mime})}

测试结果

  • 图片成功上传到阿里云OSS,但文件名是乱码(“一定是客户爸爸的Word里藏了外星文字!”)

第四章:后端收尾——PHP的“优雅”处理

Step 1:生成阿里云OSS上传签名

代码片段

// api/upload-sign.phpdate('Y-m-d\TH:i:s\Z',time()+3600),'conditions'=>[['bucket'=>$bucket],['starts-with','$key','word-images/']// 限定上传目录]];$base64Policy=base64_encode(json_encode($policy));$signature=base64_encode(hash_hmac('sha1',$base64Policy,$accessKeySecret,true));echojson_encode(['accessId'=>$accessKeyId,'policy'=>$base64Policy,'signature'=>$signature,'host'=>"https://{$bucket}.{$endpoint}",'expire'=>time()+3600]);}catch(OssException$e){http_response_code(500);echojson_encode(['error'=>$e->getMessage()]);}?>

测试结果

  • 前端拿到签名后,直接通过OSS的POST Policy上传,PHP后端只需“躺平”接收结果(“PHP果然是世界上最好的语言!”)

第五章:群友助力——QQ群里的“神仙打架”

在QQ群223813913里喊了一嗓子:
“求救!Word图片转存,前端怎么提取所有图片?”
瞬间炸出一堆大佬:

  1. @上海-张工
    “别用正则匹配HTML!用DOMParser解析更靠谱!”
    constparser=newDOMParser()constdoc=parser.parseFromString(html,'text/html')constimages=Array.from(doc.querySelectorAll('img'))
  2. @深圳-李姐
    “阿里云OSS直传可以配置回调,上传成功后自动通知你的后端!”
  3. @杭州-小学生(实际是阿里云架构师)
    “建议用docx.js直接解析Word文件,比转HTML更精准!”

最终方案

  • 前端:wangEditor5+DOMParser提取图片
  • 后端:PHP生成OSS签名 + 回调处理

第六章:大功告成——客户爸爸的“真香”现场

测试视频发给客户后,回复如下:
“这速度!这体验!比我们手动操作快10倍!明天请你喝星巴克!”
(内心OS:“星巴克?不如折现给我买机械键盘!”


今日总结

  1. 技术栈:Vue2 + wangEditor5 + PHP + 阿里云OSS
  2. 关键点
    • Word转HTML后提取图片
    • Base64转Blob上传
    • OSS直传减少服务器压力
  3. 群友价值“一个人可以走得快,但一群人可以走得远(还能一起摸鱼)!”

明日计划

  • 优化图片压缩(前端用browser-image-compression
  • 写一篇技术博客《从Word到OSS:暑假程序员の自救指南》

(签名)
苏州·秃头但快乐的程序员
2023年7月15日

P.S.:如果你们也想加入“Word图片转存”豪华套餐,欢迎来QQ群223813913,群文件里有完整代码和部署教程(“白嫖党狂喜!”

复制插件文件


安装jquery

npm install jquery

导入组件

importEfrom'wangeditor'const{$,BtnMenu,DropListMenu,PanelMenu,DropList,Panel,Tooltip}=Eimport{WordPaster}from'../../static/WordPaster/js/w'import{zyCapture}from'../../static/zyCapture/z'import{zyOffice}from'../../static/zyOffice/js/o'

初始化组件

//zyCapture ButtonclasszyCaptureBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyCapture.setEditor(this.editor).Capture();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportWordBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openDoc();}tryChangeActive(){this.active()}}//zyOffice ButtonclassexportWordBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.exportWord();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportPdfBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openPdf();}tryChangeActive(){this.active()}}//WordPaster ButtonclassWordPasterBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).Paste();}tryChangeActive(){this.active()}}//wordImport ButtonclassWordImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWord();}tryChangeActive(){this.active()}}//excelImport ButtonclassExcelImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importExcel();}tryChangeActive(){this.active()}}//ppt paster ButtonclassPPTImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importPPT();}tryChangeActive(){this.active()}}//pdf paster ButtonclassPDFImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor);WordPaster.getInstance().ImportPDF();}tryChangeActive(){this.active()}}//importWordToImg ButtonclassImportWordToImgBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWordToImg();}tryChangeActive(){this.active()}}//network paster ButtonclassNetImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor);WordPaster.getInstance().UploadNetImg();}tryChangeActive(){this.active()}}exportdefault{name:'HelloWorld',data(){return{msg:'Welcome to Your Vue.js App'}},mounted(){vareditor=newE('#editor');WordPaster.getInstance({//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203edPostUrl:"http://localhost:8891/upload.aspx",License2:"",//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936ImageUrl:"http://localhost:8891{url}",//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:"file",//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1ImageMatch:''});zyCapture.getInstance({config:{PostUrl:"http://localhost:8891/upload.aspx",License2:'',FileFieldName:"file",Fields:{uname:"test"},ImageUrl:'http://localhost:8891{url}'}})// zyoffice,// 使用前请在服务端部署zyoffice,// http://www.ncmem.com/doc/view.aspx?id=82170058de824b5c86e2e666e5be319czyOffice.getInstance({word:'http://localhost:13710/zyoffice/word/convert',wordExport:'http://localhost:13710/zyoffice/word/export',pdf:'http://localhost:13710/zyoffice/pdf/upload'})// 注册菜单E.registerMenu("zyCaptureBtn",zyCaptureBtn)E.registerMenu("WordPasterBtn",WordPasterBtn)E.registerMenu("ImportWordToImgBtn",ImportWordToImgBtn)E.registerMenu("NetImportBtn",NetImportBtn)E.registerMenu("WordImportBtn",WordImportBtn)E.registerMenu("ExcelImportBtn",ExcelImportBtn)E.registerMenu("PPTImportBtn",PPTImportBtn)E.registerMenu("PDFImportBtn",PDFImportBtn)E.registerMenu("importWordBtn",importWordBtn)E.registerMenu("exportWordBtn",exportWordBtn)E.registerMenu("importPdfBtn",importPdfBtn)//挂载粘贴事件editor.txt.eventHooks.pasteEvents.length=0;editor.txt.eventHooks.pasteEvents.push(function(){WordPaster.getInstance().SetEditor(editor).Paste();e.preventDefault();});editor.create();varedt2=newE('#editor2');//挂载粘贴事件edt2.txt.eventHooks.pasteEvents.length=0;edt2.txt.eventHooks.pasteEvents.push(function(){WordPaster.getInstance().SetEditor(edt2).Paste();e.preventDefault();return;});edt2.create();}}h1,h2{font-weight:normal;}ul{list-style-type:none;padding:0;}li{display:inline-block;margin:010px;}a{color:#42b983;}

测试前请配置图片上传接口并测试成功
接口测试
接口返回JSON格式参考

为编辑器添加按钮

components:{Editor,Toolbar},data(){return{editor:null,html:'dd',toolbarConfig:{insertKeys:{index:0,keys:['zycapture','wordpaster','pptimport','pdfimport','netimg','importword','exportword','importpdf']}},editorConfig:{placeholder:''},mode:'default'// or 'simple'}},

整合效果

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

粘贴Word

一键粘贴Word内容,自动上传Word中的图片,保留文字样式。

Word转图片

一键导入Word文件,并将Word文件转换成图片上传到服务器中。

导入PDF

一键导入PDF文件,并将PDF转换成图片上传到服务器中。

导入PPT

一键导入PPT文件,并将PPT转换成图片上传到服务器中。

上传网络图片

一键自动上传网络图片,自动下载远程服务器图片,自动上传远程服务器图片

下载示例

点击下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 8:58:34

5天精通轻量级人脸检测:从原理到落地全攻略

5天精通轻量级人脸检测&#xff1a;从原理到落地全攻略 【免费下载链接】yolov8-face 项目地址: https://gitcode.com/gh_mirrors/yo/yolov8-face 在当今计算机视觉领域&#xff0c;实时人脸检测技术正以前所未有的速度渗透到各行各业。随着边缘计算设备的普及和嵌入式…

作者头像 李华
网站建设 2026/6/15 14:32:08

探索STorM32 BGC:打造专业级影像稳定系统的开源方案

探索STorM32 BGC&#xff1a;打造专业级影像稳定系统的开源方案 【免费下载链接】storm32bgc 3-axis Brushless Gimbal Controller, based on STM32 32-bit microcontroller 项目地址: https://gitcode.com/gh_mirrors/st/storm32bgc 你是否正在寻找一款能够为无人机提…

作者头像 李华
网站建设 2026/6/15 16:49:10

3步攻克魔兽世界技能循环:GSE宏编译器解决方案

3步攻克魔兽世界技能循环&#xff1a;GSE宏编译器解决方案 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Curs…

作者头像 李华
网站建设 2026/6/14 20:33:55

突破网盘下载瓶颈:8大平台直链解析技术指南

突破网盘下载瓶颈&#xff1a;8大平台直链解析技术指南 【免费下载链接】Online-disk-direct-link-download-assistant 可以获取网盘文件真实下载地址。基于【网盘直链下载助手】修改&#xff08;改自6.1.4版本&#xff09; &#xff0c;自用&#xff0c;去推广&#xff0c;无需…

作者头像 李华
网站建设 2026/6/15 14:54:55

如何通过MediaCrawler智能采集实现多平台数据获取新方案

如何通过MediaCrawler智能采集实现多平台数据获取新方案 【免费下载链接】MediaCrawler-new 项目地址: https://gitcode.com/GitHub_Trending/me/MediaCrawler-new 在信息爆炸的时代&#xff0c;企业和研究者常常面临这样的困境&#xff1a;如何高效、合规地获取分散在…

作者头像 李华