医院信息化平台如何实现PDF文档中图片的自动转存至CKEDITOR?

企业网站后台管理系统富文本编辑功能扩展开发记录

一、需求确认与目标拆解

作为江苏某网络公司前端开发工程师,近期接到客户在企业网站后台管理系统文章发布模块的功能扩展需求:

  1. 核心功能
    • Word粘贴:支持从Word复制内容(含表格/字体/颜色等样式)粘贴至CKEditor4,图片自动上传至服务器(二进制存储)
    • 多格式导入:支持Word/Excel/PPT/PDF导入,保留完整样式与图片
    • 微信公众号粘贴:实现与Word粘贴相同的格式保留与图片处理逻辑
  2. 技术约束
    • 前端:Vue2-cli + CKEditor4(需通过插件扩展)
    • 后端:Java Spring Boot(文件上传接口)
    • 存储:初期单据存储服务器(本地文件系统),后期迁移至阿里云/华为云等对象存储
    • 图片存储:二进制流(禁用BASE64)

二、技术选型与插件评估

(一)Word粘贴功能实现方案

  1. CKEditor4插件调研
    插件名称版本兼容性功能特点局限性
    WordPaste4.x轻量级插件,支持Word粘贴与图片自动上传,需配合后端上传接口需手动处理样式过滤规则
    PasteFromWord内置CKEditor4原生支持,但需额外配置保留样式,图片默认转为BASE64不满足二进制存储需求
    zyOffice商业版全功能文档处理,支持Word/Excel/PPT导入与样式保留,图片自动上传需商业授权,学习成本较高
    泽优WordPaster源码版全功能文档处理,支持Word/Excel/PPT导入与样式保留,图片自动上传

兼容多种编辑器
兼容任意开发语言
完全开源(下载源码)
免费技术支持(QQ群:223813913) | 需要终端安装插件 |

  1. 最终选型
    • 核心方案WordPaste插件 + 自定义样式过滤逻辑
    • 理由
      • 免费开源,与Vue2集成简单
      • 支持通过onPaste事件拦截处理图片二进制流
      • 可通过config.pasteFilter精确控制样式保留级别

(二)多格式导入功能实现方案

  1. 文档解析库对比

    库名称支持格式优势风险点
    Apache POIWord/ExcelJava生态成熟,社区支持完善PPT/PDF解析能力弱
    Aspose.Words全格式商业库,解析精度高需购买授权,成本较高
    docx4jWord/PPT开源,支持复杂文档结构学习曲线陡峭
  2. 混合方案

    • 前端:通过CKEditor4的fileTools扩展实现基础导入按钮
    • 后端
      • Word/Excel:Apache POI解析 + HTML转换
      • PPT:Apache POI提取幻灯片为图片序列
      • PDF:PDFBox提取文本 + iText提取图片

(三)图片存储架构设计

二进制流
用户粘贴Word内容
图片处理
上传至单据存储服务器
生成访问URL
替换编辑器中的临时图片
后期迁移
阿里云OSS/华为云OBS

三、开发实施过程

(一)环境准备

  1. 前端依赖安装

    npm install @ckeditor/ckeditor4-vue --save
    npm install wordpaste --save  # 假设存在适配的WordPaste插件
    
  2. 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
        }
      }
    }
    

(二)核心功能开发

  1. 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);
    };
    
  2. 多格式导入接口实现

    // 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");
      }
    }
    
  3. 微信公众号内容处理

    // 监听粘贴事件
    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);
      });
    }
    

(三)存储服务集成

  1. 本地存储实现

    // 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();
      }
    }
    
  2. 阿里云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秒内完成上传与解析

(二)性能优化方案

  1. 图片处理

    • 使用canvas对导入的PPT图片进行压缩(质量85%)
    • 为Word/Excel生成的图片添加lazyload属性
  2. 内存管理

    // 导入完成后清理临时DOM
    const cleanup = () => {
      const tempDiv = document.getElementById('temp-parser');
      if (tempDiv) tempDiv.remove();
    };
    

五、交付成果与经验总结

  1. 核心交付物

    • WordPasteVue插件(基于CKEditor4二次开发)
    • 文档解析服务(Spring Boot微服务)
    • 存储抽象层(支持本地/OSS无缝切换)
  2. 技术亮点

    • 通过DOMParser实现精准的样式提取
    • 使用Web Worker处理大文件导入避免界面卡顿
    • 设计插件化架构便于后续扩展(如支持Google Docs粘贴)
  3. 后续改进方向

    • 增加对Office 365文档(.docx/.xlsx)的实时协作支持
    • 实现导入文档的版本历史记录功能
    • 优化移动端粘贴体验(处理微信/钉钉等APP的特殊剪贴板格式)

本次开发验证了Vue2 + CKEditor4在复杂富文本处理场景下的可行性,为后续企业级CMS系统开发积累了可复用的技术资产。

复制插件

说明:此教程以CKEditor4.x为例,使用其他编辑器的查看对应教程。
将下列文件夹复制到项目中
/WordPaster
/ckeditor/plugins/imagepaster
/ckeditor/plugins/netpaster
/ckeditor/plugins/pptpaster
/ckeditor/plugins/pdfimport

上传插件

wordpaster文件夹

上传插件文件夹

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

在工具栏中增加插件按钮

插件按钮

引用js

引用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.请先测试您的接口:点击查看详细教程

功能演示

编辑器界面

image

导入Word文档,支持doc,docx

粘贴Word和图片

导入Excel文档,支持xls,xlsx

粘贴Word和图片

粘贴Word

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

Word转图片

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

导入PDF

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

导入PPT

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

上传网络图片

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

下载示例

点击下载完整示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值