news 2026/4/20 14:52:25

wangEditor word粘贴支持超链接和锚点定位

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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/4/17 23:18:54

SpringBoot大文件上传插件的开源代码与商业应用对比

政府招投标项目大文件传输解决方案设计与实现 项目背景与需求分析 作为本次政府招投标项目的开发负责人&#xff0c;我负责解决100G级大文件传输的技术难题。项目要求支持包括IE8在内的所有主流浏览器&#xff0c;同时必须适配信创国产化环境&#xff08;统信UOS、中标麒麟、…

作者头像 李华
网站建设 2026/4/15 20:07:17

风电叶片巡检:GLM-4.6V-Flash-WEB发现表面腐蚀区域

风电叶片巡检&#xff1a;GLM-4.6V-Flash-WEB发现表面腐蚀区域 在广袤的风电场中&#xff0c;风机叶片日复一日地迎风旋转&#xff0c;将自然之力转化为清洁电力。然而&#xff0c;这些钢铁之翼也正承受着严苛环境的持续侵蚀——盐雾、紫外线、温差、沙尘……任何微小的表面损伤…

作者头像 李华
网站建设 2026/4/17 22:38:15

红树林恢复项目:GLM-4.6V-Flash-WEB统计新生植株数量

红树林恢复项目中的AI实践&#xff1a;用GLM-4.6V-Flash-WEB自动统计新生植株 在广东沿海的一片滩涂上&#xff0c;生态修复团队正面临一个老问题&#xff1a;每季度都要派人徒步数公里&#xff0c;弯腰辨认、手工记录新生的红树幼苗。潮湿、蚊虫、视野遮挡让这项工作不仅辛苦&…

作者头像 李华
网站建设 2026/4/20 8:16:41

腾讯云某业务基于 DeepFlow 的可观测性实践

摘要&#xff1a;本文分享了腾讯云某业务基于 DeepFlow 的可观测性实践。面对复杂的业务服务&#xff08;800&#xff09;和多样的编程语言&#xff0c;腾讯云某业务团队选择了 DeepFlow 作为跨语言、无侵入的可观测技术。与其他技术&#xff08;如 Hubble 和 Pixie&#xff09…

作者头像 李华
网站建设 2026/4/16 1:52:16

外汇 API 是什么?从行情接口到量化研究的完整视角

刚开始做外汇程序化交易时&#xff0c;我经常问自己一个看似简单的问题&#xff1a;外汇市场没有统一交易所&#xff0c;那所谓的“外汇 API”到底从哪里来的&#xff1f; 很多教程会直接告诉你&#xff1a;“用外汇 API 拉行情就行”&#xff0c;但真正做过量化研究或搭建行情…

作者头像 李华
网站建设 2026/4/16 17:01:09

springboot+ssm体育场地器材管理系统vue

目录系统概述技术架构核心功能创新与优化开发技术核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统概述 体育…

作者头像 李华