企业网站后台管理系统富文本编辑功能扩展开发记录
一、需求确认与目标拆解
作为江苏某网络公司前端开发工程师,近期接到客户在企业网站后台管理系统文章发布模块的功能扩展需求:
- 核心功能:
- Word粘贴:支持从Word复制内容(含表格/字体/颜色等样式)粘贴至CKEditor4,图片自动上传至服务器(二进制存储)
- 多格式导入:支持Word/Excel/PPT/PDF导入,保留完整样式与图片
- 微信公众号粘贴:实现与Word粘贴相同的格式保留与图片处理逻辑
- 技术约束:
- 前端:Vue2-cli + CKEditor4(需通过插件扩展)
- 后端:Java Spring Boot(文件上传接口)
- 存储:初期单据存储服务器(本地文件系统),后期迁移至阿里云/华为云等对象存储
- 图片存储:二进制流(禁用BASE64)
二、技术选型与插件评估
(一)Word粘贴功能实现方案
- CKEditor4插件调研
插件名称 版本兼容性 功能特点 局限性 WordPaste 4.x 轻量级插件,支持Word粘贴与图片自动上传,需配合后端上传接口 需手动处理样式过滤规则 PasteFromWord 内置 CKEditor4原生支持,但需额外配置保留样式,图片默认转为BASE64 不满足二进制存储需求 zyOffice 商业版 全功能文档处理,支持Word/Excel/PPT导入与样式保留,图片自动上传 需商业授权,学习成本较高 泽优WordPaster 源码版 全功能文档处理,支持Word/Excel/PPT导入与样式保留,图片自动上传
兼容多种编辑器
兼容任意开发语言
完全开源(下载源码)
免费技术支持(QQ群:223813913) | 需要终端安装插件 |
- 最终选型
- 核心方案:
WordPaste插件 + 自定义样式过滤逻辑 - 理由:
- 免费开源,与Vue2集成简单
- 支持通过
onPaste事件拦截处理图片二进制流 - 可通过
config.pasteFilter精确控制样式保留级别
- 核心方案:
(二)多格式导入功能实现方案
-
文档解析库对比
库名称 支持格式 优势 风险点 Apache POI Word/Excel Java生态成熟,社区支持完善 PPT/PDF解析能力弱 Aspose.Words 全格式 商业库,解析精度高 需购买授权,成本较高 docx4j Word/PPT 开源,支持复杂文档结构 学习曲线陡峭 -
混合方案
- 前端:通过CKEditor4的
fileTools扩展实现基础导入按钮 - 后端:
- Word/Excel:Apache POI解析 + HTML转换
- PPT:Apache POI提取幻灯片为图片序列
- PDF:PDFBox提取文本 + iText提取图片
- 前端:通过CKEditor4的
(三)图片存储架构设计
三、开发实施过程
(一)环境准备
-
前端依赖安装
npm install @ckeditor/ckeditor4-vue --save npm install wordpaste --save # 假设存在适配的WordPaste插件 -
CKEditor4基础配置
// main.js import CKEditor from '@ckeditor/ckeditor4-vue'; Vue.use(CKEditor); // ArticleEdit.vue data() { return { editorConfig: { extraPlugins: 'wordpaste', toolbar: [ { name: 'clipboard', items: ['wordpaste', 'Undo', 'Redo'] } ], // 禁用默认的Word粘贴过滤器 pasteFromWordRemoveFontStyles: false, pasteFromWordRemoveStyles: false } } }
(二)核心功能开发
-
WordPaste插件二次开发
// 覆盖插件默认行为,强制使用二进制上传 CKEDITOR.plugins.registered.wordpaste.onPaste = function(editor, html) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const images = doc.querySelectorAll('img[src^="data:image"]'); images.forEach(async img => { const blob = await fetch(img.src).then(r => r.blob()); const formData = new FormData(); formData.append('file', blob, 'word-image.png'); // 调用后端上传接口 const response = await axios.post('/api/upload', formData); img.src = response.data.url; // 替换为服务器URL }); editor.insertHtml(doc.body.innerHTML); }; -
多格式导入接口实现
// Spring Boot Controller @PostMapping("/import/docx") public ResponseEntity importDocx(@RequestParam("file") MultipartFile file) { try (XWPFDocument doc = new XWPFDocument(file.getInputStream())) { StringBuilder html = new StringBuilder(""); // 解析段落样式 doc.getParagraphs().forEach(p -> { XWPFParagraph paragraph = (XWPFParagraph) p; String style = paragraph.getStyle(); html.append("") .append(p.getText()).append(""); }); // 解析表格(简化版) doc.getTables().forEach(table -> { html.append(""); table.getRows().forEach(row -> { html.append(""); row.getTableCells().forEach(cell -> { html.append(""); }); html.append(""); }); html.append("").append(cell.getText()).append(""); }); html.append(""); return ResponseEntity.ok(html.toString()); } catch (IOException e) { throw new RuntimeException("Document parsing failed"); } } -
微信公众号内容处理
// 监听粘贴事件 mounted() { document.addEventListener('paste', async (e) => { if (!e.target.closest('.cke_editable')) return; const html = e.clipboardData.getData('text/html'); if (!html) return; const doc = new DOMParser().parseFromString(html, 'text/html'); const images = doc.querySelectorAll('img[src*="wx.qlogo.cn"]'); // 微信公众号图片特殊处理 await Promise.all(Array.from(images).map(async img => { const res = await axios.get(img.src, { responseType: 'blob' }); const formData = new FormData(); formData.append('file', res.data, 'wechat-img.jpg'); const uploadRes = await axios.post('/api/upload', formData); img.src = uploadRes.data.url; })); this.$refs.editor.insertHtml(doc.body.innerHTML); }); }
(三)存储服务集成
-
本地存储实现
// FileUploadController.java @PostMapping("/upload") public ResponseEntity> uploadFile(@RequestParam("file") MultipartFile file) { try { String fileName = UUID.randomUUID() + "." + StringUtils.getFilenameExtension(file.getOriginalFilename()); Path filePath = Paths.get("/opt/uploads/" + fileName); Files.write(filePath, file.getBytes()); Map response = new HashMap<>(); response.put("url", "/uploads/" + fileName); return ResponseEntity.ok(response); } catch (IOException e) { return ResponseEntity.status(500).build(); } } -
阿里云OSS迁移准备
// OSSConfig.java @Configuration public class OSSConfig { @Value("${oss.endpoint}") private String endpoint; @Bean public OSS ossClient() { return new OSSClientBuilder().build(endpoint, System.getenv("OSS_ACCESS_KEY"), System.getenv("OSS_SECRET_KEY")); } }
四、测试与优化
(一)功能测试矩阵
| 测试场景 | 预期结果 |
|---|---|
| Word粘贴(含表格) | 表格边框/颜色/合并单元格正确保留,图片可访问 |
| Excel导入 | 工作表转为HTML表格,复杂公式显示为文本 |
| 微信公众号图片粘贴 | 自动替换CDN链接为本地URL,保持原图尺寸 |
| 大文件导入(>10MB) | 显示进度条,10秒内完成上传与解析 |
(二)性能优化方案
-
图片处理:
- 使用
canvas对导入的PPT图片进行压缩(质量85%) - 为Word/Excel生成的图片添加
lazyload属性
- 使用
-
内存管理:
// 导入完成后清理临时DOM const cleanup = () => { const tempDiv = document.getElementById('temp-parser'); if (tempDiv) tempDiv.remove(); };
五、交付成果与经验总结
-
核心交付物:
WordPasteVue插件(基于CKEditor4二次开发)- 文档解析服务(Spring Boot微服务)
- 存储抽象层(支持本地/OSS无缝切换)
-
技术亮点:
- 通过DOMParser实现精准的样式提取
- 使用Web Worker处理大文件导入避免界面卡顿
- 设计插件化架构便于后续扩展(如支持Google Docs粘贴)
-
后续改进方向:
- 增加对Office 365文档(.docx/.xlsx)的实时协作支持
- 实现导入文档的版本历史记录功能
- 优化移动端粘贴体验(处理微信/钉钉等APP的特殊剪贴板格式)
本次开发验证了Vue2 + CKEditor4在复杂富文本处理场景下的可行性,为后续企业级CMS系统开发积累了可复用的技术资产。
复制插件
说明:此教程以CKEditor4.x为例,使用其他编辑器的查看对应教程。
将下列文件夹复制到项目中
/WordPaster
/ckeditor/plugins/imagepaster
/ckeditor/plugins/netpaster
/ckeditor/plugins/pptpaster
/ckeditor/plugins/pdfimport
上传插件

上传插件文件夹
将imagepaster,netpaster文件夹上传到现有项目ckeditor/plugins目录中

在工具栏中增加插件按钮

引用js

初始化控件
WordPaster.getInstance({
//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203ed
PostUrl: api,
//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936
ImageUrl: "",
//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45
FileFieldName: "file",
//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1
ImageMatch: '',
Cookie: 'PHPSESSID='
});//加载控件
配置上传接口

注意
1.如果接口字段名称不是file,请配置FileFieldName。ueditor接口中使用的upfile字段

点击查看详细教程
配置ImageMatch
用于匹配JSON数据,

点击查看详细教程
配置ImageUrl
用于为图片增加域名前缀

点击查看详细教程
配置Session
如果接口有权限验证(登陆验证,SESSION验证),请配置COOKIE。或取消权限验证。
参考:点击查看详细教程
说明
1.请先测试您的接口:点击查看详细教程
功能演示
编辑器界面

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

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

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

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

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

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

1125

被折叠的 条评论
为什么被折叠?



