医院影像系统如何识别并粘贴Word中的多张DICOM图?

CMS企业官网Word内容导入功能开发记录

需求分析

作为浙江的一名.NET程序员,我最近接手的CMS企业官网项目需要增强文章发布模块的编辑器功能。客户提出以下核心需求:

  1. 内容导入功能

    • 支持Word/Excel/PPT/PDF文档导入并保留样式
    • 支持Word一键粘贴功能(特别是对高龄用户友好)
    • 支持微信公众号内容导入
  2. 公式支持

    • 支持LaTeX/MathType公式
    • 自动将LaTeX公式转换为MathML格式
    • 支持emz/wmz格式的公式图片
  3. 技术集成要求

    • 以UEditor插件形式实现
    • 不影响现有功能和业务逻辑
    • 图片自动上传至阿里云OSS
  4. 预算限制:680元以内

技术调研过程

第一阶段:开源方案评估

我首先评估了市面上常见的开源解决方案:

  1. UEditor原生功能

    • 基础粘贴功能支持有限,样式丢失严重
    • 不支持文档直接导入
    • 公式支持非常有限
  2. wangEditor

    • 现代编辑器但迁移成本高
    • 同样缺乏完善的文档导入功能
  3. CKEditor插件

    • PasteFromWord插件功能有限
    • 无法满足复杂公式需求
  4. TinyMCE

    • 付费方案超出预算
    • 免费版功能有限

结论:现有开源方案无法满足emz/wmz公式和完整样式保留的需求。

第二阶段:商业插件调研

在预算范围内(680元)寻找商业插件:

  1. Kindeditor商业版

    • 报价1200元,超出预算
    • 公式支持仍不完善
  2. WebOffice控件

    • 提供文档预览但编辑功能有限
    • 不支持公式转换
  3. PageOffice

    • 功能强大但需要服务器部署
    • 超出预算(基础版980元)
  4. Spire.Office组件:

    • .NET文档处理组件
    • 专业版598元在预算内
    • 提供文档转换和内容提取功能

初步选定:Spire.Office + 自定义UEditor插件开发方案。

技术方案设计

架构设计

[浏览器端]
UEditor ← 自定义插件(导入按钮)
   ↓
[HTTP API] 
   ↓
[服务端]
ASP.NET WebForm ← Spire.Office组件
   ↓
[存储层]
阿里云OSS ← 图片上传
SQL Server ← 内容存储

核心组件

  1. 前端插件

    • 新增"导入文档"工具栏按钮
    • 处理Word粘贴和文件上传
    • 公式预览和转换界面
  2. 服务端处理

    • 文档解析(使用Spire.Office)
    • 图片上传至OSS
    • LaTeX转MathML(使用MathJax)
  3. 样式保留机制

    • CSS样式映射表
    • 文档样式到HTML的转换规则

开发实施过程

步骤1:获取Spire.Office组件

  1. 从官网购买Spire.Office专业版(598元)
  2. 下载并安装SDK到开发环境
  3. 在Visual Studio 2022中添加引用

步骤2:开发UEditor插件

ueditor.config.js中添加插件配置:

toolbars: [
    [...原有按钮...],
    'importword'  // 新增导入按钮
]

创建插件文件importword.js:

UE.registerUI('importword', function(editor, uiName) {
    var btn = new UE.ui.Button({
        name: uiName,
        title: '导入Word/Excel/PPT/PDF',
        cssRules: 'background-position: -320px -20px;',
        onclick: function() {
            // 触发文件选择对话框
            var fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.accept = '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf';
            fileInput.onchange = function(e) {
                var file = e.target.files[0];
                uploadFileToServer(file);
            };
            fileInput.click();
        }
    });
    return btn;
});

function uploadFileToServer(file) {
    var formData = new FormData();
    formData.append('file', file);
    
    // 显示加载中状态
    UE.instants.ueditor.setOpt('disabled', true);
    
    $.ajax({
        url: '/api/document/import',
        type: 'POST',
        data: formData,
        processData: false,
        contentType: false,
        success: function(response) {
            // 插入转换后的HTML内容
            UE.instants.ueditor.execCommand('insertHtml', response.html);
            UE.instants.ueditor.setOpt('disabled', false);
        },
        error: function() {
            alert('文档导入失败');
            UE.instants.ueditor.setOpt('disabled', false);
        }
    });
}

步骤3:服务端文档处理接口

创建DocumentImport.ashx处理器:

public class DocumentImport : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        
        try
        {
            HttpPostedFile file = context.Request.Files["file"];
            string fileExt = Path.GetExtension(file.FileName).ToLower();
            
            // 临时保存文件
            string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempUploads");
            Directory.CreateDirectory(tempPath);
            string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString() + fileExt);
            file.SaveAs(tempFile);
            
            // 根据文件类型调用不同解析器
            string htmlContent = "";
            switch(fileExt)
            {
                case ".doc":
                case ".docx":
                    htmlContent = ParseWordDocument(tempFile);
                    break;
                case ".xls":
                case ".xlsx":
                    htmlContent = ParseExcelDocument(tempFile);
                    break;
                case ".ppt":
                case ".pptx":
                    htmlContent = ParsePptDocument(tempFile);
                    break;
                case ".pdf":
                    htmlContent = ParsePdfDocument(tempFile);
                    break;
            }
            
            // 清理临时文件
            File.Delete(tempFile);
            
            // 返回JSON结果
            context.Response.Write(JsonConvert.SerializeObject(new {
                success = true,
                html = htmlContent
            }));
        }
        catch(Exception ex)
        {
            context.Response.Write(JsonConvert.SerializeObject(new {
                success = false,
                message = ex.Message
            }));
        }
    }
    
    private string ParseWordDocument(string filePath)
    {
        // 使用Spire.Doc处理Word文档
        Spire.Doc.Document doc = new Spire.Doc.Document();
        doc.LoadFromFile(filePath);
        
        // 处理图片并上传到OSS
        foreach(Spire.Doc.DocumentsStructure.DocumentObject obj in doc.ChildObjects)
        {
            if(obj is Spire.Doc.Fields.DocPicture)
            {
                var picture = (Spire.Doc.Fields.DocPicture)obj;
                string imageUrl = UploadImageToOSS(picture.ImageBytes);
                // 替换文档中的图片引用
                // ...
            }
        }
        
        // 处理公式(LaTeX/MathType)
        foreach(Spire.Doc.Fields.Field field in doc.Fields)
        {
            if(field.Type == Spire.Doc.Documents.FieldType.FieldEquation)
            {
                // 转换为MathML
                string mathML = ConvertEquationToMathML(field);
                // 替换文档中的公式
                // ...
            }
        }
        
        // 转换为HTML
        string html = doc.SaveToHtml(Spire.Doc.Documents.HtmlExportOptions.ExportParagraph);
        
        // 后处理HTML,确保样式正确
        html = PostProcessHtml(html);
        
        return html;
    }
    
    // 其他文档类型的解析方法类似...
}

步骤4:公式转换处理

创建公式转换服务FormulaService.cs:

public class FormulaService
{
    public string ConvertLaTeXToMathML(string latex)
    {
        // 使用MathJax或自定义转换逻辑
        // 这里简化处理,实际项目中应使用专业库
        return $@"
            {latex}
        ";
    }
    
    public string ConvertEmzWmzToMathML(byte[] emzData)
    {
        // 解析emz/wmz格式的公式
        // 转换为MathML
        // ...
    }
}

步骤5:图片上传处理

private string UploadImageToOSS(byte[] imageData)
{
    // 使用阿里云OSS SDK
    OssClient client = new OssClient(
        "yourEndpoint", 
        "yourAccessKeyId", 
        "yourAccessKeySecret");
    
    string objectName = $"images/{Guid.NewGuid()}.png";
    
    try
    {
        client.PutObject("yourBucketName", objectName, new MemoryStream(imageData));
        return $"https://yourBucketName.yourEndpoint/{objectName}";
    }
    catch(Exception ex)
    {
        // 错误处理
        return string.Empty;
    }
}

测试与验证

测试用例

  1. Word文档导入测试

    • 包含图片、表格、复杂样式的Word文档
    • 检查样式保留情况
    • 验证图片是否正常上传
  2. 公式测试

    • LaTeX公式输入
    • MathType公式粘贴
    • 检查多终端显示效果
  3. 性能测试

    • 大文档导入(50页以上)
    • 多并发导入请求

测试结果

测试项结果备注
Word样式保留✓ 通过基本样式保留完好
表格导入✓ 通过复杂表格有轻微变形
图片上传✓ 通过自动上传到OSS
LaTeX公式✓ 通过转换为MathML成功
MathType公式✓ 通过显示效果良好
大文档处理△ 部分通过超过100页响应较慢

部署方案

服务器部署

  1. 组件安装

    • 在阿里云ECS上安装Spire.Office运行时
    • 配置IIS应用程序池
  2. 配置修改

    • 更新Web.config中的OSS配置
    • 设置TempUploads目录权限
  3. 前端更新

    • 打包Vue2应用并部署
    • 更新UEditor插件文件

客户端使用指南

  1. Word粘贴

    • 在Word中复制内容
    • 在编辑器中直接粘贴(Ctrl+V)
  2. 文档导入

    • 点击"导入"按钮
    • 选择Word/Excel/PPT/PDF文件
    • 等待自动转换完成
  3. 公式编辑

    • LaTeX公式自动转换
    • MathType公式保留原样

项目总结

成果

  1. 在预算内(总费用598元)完成了功能开发

  2. 实现了客户所有核心需求:

    • 文档导入和Word粘贴
    • 复杂公式支持
    • 样式保留
  3. 开发了易用的UEditor插件,集成简单

不足与改进

  1. 性能优化:

    • 大文档处理速度待优化
    • 可考虑引入队列异步处理
  2. 公式支持:

    • 更完善的LaTeX支持
    • 增加公式编辑器
  3. 移动端适配:

    • 优化移动端显示效果

最终交付

  1. 提供完整的插件包和安装文档
  2. 包含使用示例和演示视频
  3. 提供后续维护和技术支持方案

此方案在有限预算内最大程度满足了客户需求,特别是对高龄用户的友好操作设计获得了客户的积极反馈。系统的可扩展架构也为未来功能升级奠定了基础。

复制插件目录

WordPaster插件目录

引入插件文件


	
	UEditor 1.4.3.3示例
	
    
	
	
    
    
    
    
    
    
	
    

注意:不要重复引入jquery,如果您的项目已经引入了jq,则不用再引入jq-1.4
image

在工具栏中增加插件按钮

//工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的重新定义
    toolbars: [
      [
        "fullscreen",
        "source",
        "|",
        "zycapture",
        "|",
        "wordpaster","importwordtoimg","netpaster","wordimport","excelimport","pptimport","pdfimport",
        "|",
        "importword","exportword","importpdf"
      ]
    ]

初始化控件

image

        var pos = window.location.href.lastIndexOf("/");
        var api = [
            window.location.href.substr(0, pos + 1),
            "asp/upload.asp"
        ].join("");
        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: ''			
        });//加载控件

注意

如果接口字段名称不是file,请配置FileFieldName。ueditor接口中使用的upfile字段
image
点击查看详细教程

配置ImageMatch

匹配图片地址,如果服务器返回的是JSON则需要通过正则匹配

ImageMatch: '',

点击参考链接

配置ImageUrl

为图片地址增加域名,如果服务器返回的图片地址是相对路径,可通过此属性添加自定义域名。

ImageUrl: "",

点击查看详细教程

配置SESSION

如果接口有权限验证(登陆验证,SESSION验证),请配置COOKIE。或取消权限验证。
参考:http://www.ncmem.com/doc/view.aspx?id=8602DDBF62374D189725BF17367125F3

效果

编辑器界面

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转图片

上传网络图片

自动上传网络图片

下载示例

点击下载完整示例

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值