2026/5/13 22:49:07
网站建设
项目流程
做招聘网站怎么设计框架,免费wap建站的网址是什么了,个人网站风格设计,阿里巴巴网站是怎么做的《长沙大二码农的暑假暴走开发记#xff1a;Word图片转存功能の奇幻漂流》
Day1#xff1a;需求诞生——从“懒癌晚期”到“技术狂魔”
“妈#xff01;我暑假要搞个大事#xff01;” 当我把“Word图片一键转存”的PPT甩在家庭群时#xff0c;我妈回了个“#xff1f;…《长沙大二码农的暑假暴走开发记Word图片转存功能の奇幻漂流》Day1需求诞生——从“懒癌晚期”到“技术狂魔”“妈我暑假要搞个大事”当我把“Word图片一键转存”的PPT甩在家庭群时我妈回了个“”我爸回了句“6”。其实动机很简单作为学生会宣传部“专业P图师”自封的每次从Word里抠图都要经历右键另存为 → 弹出“此图片受保护”截图 → 发现像素糊成马赛克跪求原图 → 被设计师同学嘲讽“菜狗”怒而拍桌“我要写个程序让Word图片自动上传到服务器”然后偷偷幻想开学后能不能靠这个装逼接私活Day2-3组件狩猎——在GitHub的海洋里“捞鱼”前端三件套Vue2-cli学校教过勉强能用反正复制粘贴代码不会错wangEditor比CKEditor简单但文档里“图片上传”部分写着“自行实现后端接口”……内心OS“所以爱会消失对吗”免费解析库mammoth.js能提Word图片但输出是Base64上传会卡死浏览器测试时直接让Chrome崩溃3次docx-previewGitHub 1.8k星但示例代码全是React的作为Vue仔当场裂开pdf.js处理PDF还行但Word“不你不想”强行适配后报错像放鞭炮最终妥协前端用FileReader读Word文件 → 解析ZIP结构毕竟docx本质是ZIP包→ 手动捞word/media/下的图片后端PHP接收图片流 → 存到阿里云OSS参考了某位大佬的博客代码抄得飞起QQ群交流加入群223813913后发现大佬们都在聊“K8s部署”“微服务”而我“各位大佬怎么用PHP把图片存到OSS啊”群友“建议重开”Day4-7开发实录——从“Hello World”到“Hello Bug”Step1前端搞事情用JSZip解压Word文件结果发现// 代码片段捞图片的暴力写法constzipnewJSZip(file);constimages[];zip.folder(word/media/).forEach((path,file){if(/\.(png|jpg|jpeg)$/.test(path)){images.push(file.async(base64));// 后来发现base64上传会炸}});Bug1word/media/可能不存在 → 加了个try-catch优雅.jpgBug2图片顺序乱套 → 改用Array.sort()按文件名排序结果发现Word图片名是image1.png、image10.png…“微软你礼貌吗”集成wangEditor上传// 伪代码把图片塞进编辑器consteditornewwangEditor(#editor);editor.config.customUploadImgasync(files){constformDatanewFormData();formData.append(file,files[0]);// 实际要传所有图片…懒得改constresawaitaxios.post(/upload,formData);editor.cmd.do(insertHTML,);};Bug3wangEditor的customUploadImg一次只能传一张图 → 改用Promise.all批量上传手动狗头Step2后端接盘PHP接收图片流// 极简版上传接口实际有安全校验…大概$file$_FILES[file];$ossnewOssClient($key,$secret,$endpoint);$oss-putObject(my-bucket,$file[name],fopen($file[tmp_name],r));echojson_encode([urlhttps://oss-cn-hangzhou.aliyuncs.com/my-bucket/.$file[name]]);Bug4文件名冲突 → 用uniqid()生成随机名结果OSS里全是64a1b2c3d4e5f.png这种乱码Bug5MySQL存元数据时字段长度不够 → 把VARCHAR(255)改成TEXT“设计数据库时我在睡觉”Step3阿里云OSS配置创建Bucket → 设置跨域规则CORS→ 生成AccessKey“千万别泄露”测试时发现上传失败 → 查日志发现是**“签名错误”** → 原来是PHP SDK版本太旧…解决方案composerrequire aliyuncs/oss-sdk-php:^2.3# 升级到最新版Day8-10测试与部署——从“本地跑通”到“线上炸服”本地测试用Word写了个测试文档插了5张图包括1张GIF动图前端解析 → 上传 → 编辑器显示 →“成了”激动到打翻可乐部署到阿里云ECSNginx配置反向代理 → PHP-FPM跑后端 → MySQL存数据测试时发现“上传大文件会超时”解决方案修改php.iniupload_max_filesize 50M post_max_size 50MNginx加超时设置client_max_body_size 50M; proxy_read_timeout 300s;最终效果上传10MB的Word → 前端解析2秒 → 后端上传5秒 → 编辑器显示图片“虽然慢但至少能用了”老板感动到哭.jpgDay11总结与装逼技术栈回顾前端Vue2 wangEditor JSZip后端PHP OSS SDK数据库MySQL存图片URL和元数据服务器阿里云ECS学生机免费版群内装逼“大佬们我搞定了Word图片转存虽然代码很烂…”群友“上GitHub”我“算了怕被喷…”实际是懒未来计划优化解析速度用Web Worker支持Excel/PPT的图片提取“先鸽着”写个博客吹牛“正在写…”最后吐槽“原来开发就是‘抄代码 → 改Bug → 抄代码 → 改Bug’的无限循环…”但看到编辑器里显示的图片时还是觉得…“真香”完P.S.欢迎长沙的同行约饭交流QQ群223813913群文件有源码但**“慎用”**复制插件文件安装jquerynpm install jquery导入组件importEfromwangeditorconst{$,BtnMenu,DropListMenu,PanelMenu,DropList,Panel,Tooltip}Eimport{WordPaster}from../../static/WordPaster/js/wimport{zyCapture}from../../static/zyCapture/zimport{zyOffice}from../../static/zyOffice/js/o初始化组件//zyCapture ButtonclasszyCaptureBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){window.zyCapture.setEditor(this.editor).Capture();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportWordBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openDoc();}tryChangeActive(){this.active()}}//zyOffice ButtonclassexportWordBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.exportWord();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportPdfBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openPdf();}tryChangeActive(){this.active()}}//WordPaster ButtonclassWordPasterBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).Paste();}tryChangeActive(){this.active()}}//wordImport ButtonclassWordImportBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWord();}tryChangeActive(){this.active()}}//excelImport ButtonclassExcelImportBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importExcel();}tryChangeActive(){this.active()}}//ppt paster ButtonclassPPTImportBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importPPT();}tryChangeActive(){this.active()}}//pdf paster ButtonclassPDFImportBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor);WordPaster.getInstance().ImportPDF();}tryChangeActive(){this.active()}}//importWordToImg ButtonclassImportWordToImgBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-e-menu>)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWordToImg();}tryChangeActive(){this.active()}}//network paster ButtonclassNetImportBtnextendsBtnMenu{constructor(editor){const$elemE.$(div classw-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(){vareditornewE(#editor);WordPaster.getInstance({//上传接口http://www.ncmem.com/doc/view.aspx?idd88b60a2b0204af1ba62fa66288203edPostUrl:http://localhost:8891/upload.aspx,License2:,//为图片地址增加域名http://www.ncmem.com/doc/view.aspx?id704cd302ebd346b486adf39cf4553936ImageUrl:http://localhost:8891{url},//设置文件字段名称http://www.ncmem.com/doc/view.aspx?idc3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:file,//提取图片地址http://www.ncmem.com/doc/view.aspx?id07e3f323d22d4571ad213441ab8530d1ImageMatch:});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?id82170058de824b5c86e2e666e5be319czyOffice.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.length0;editor.txt.eventHooks.pasteEvents.push(function(){WordPaster.getInstance().SetEditor(editor).Paste();e.preventDefault();});editor.create();varedt2newE(#editor2);//挂载粘贴事件edt2.txt.eventHooks.pasteEvents.length0;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转换成图片上传到服务器中。上传网络图片一键自动上传网络图片自动下载远程服务器图片自动上传远程服务器图片下载示例点击下载完整示例