Tinymce富文本添加word导入的支持

发布时间 2023-11-17 17:39:11作者: SpringCore

在前端使用导入Word文档并自动解析成html再插入到tinymce编辑器中,可以使用mammoth.js识别Word内容转换为Html并set到编辑器中,使用mammoth只可解析.docx格式的Word,目前的mammoth不支持.doc格式,后续升级也许会加上解析doc的功能。
mammoth.js转换word为html丢失了好多样式,因此不推荐使用,建议统一交由后端转换
如果使用了是Tinymce5,建议直接使用https://github.com/tinymce-plugin/tinymce-plugin 这个插件,效果也不错,但是只支持docx,官方示例地址:https://tinymce-plugin.gitee.io/packages/tp-importword/tpImporatwordDemo.html

为什么解析不了.doc
  • .docx 格式的 Word 文档是一种基于 XML 和 ZIP 压缩技术的文件格式,其文件结构相对固定并且较为简单,可以通过一些开源的 JavaScript 库进行解析和转换。`
    ·
  • .doc 格式的 Word 文档是一种相对来说版本比较老一点并且是二进制格式的文件,文件结构比较复杂,具有较高的私有性和细节,需要专用微软 Office 应用程序才能完整读取。

Tinymce 支持调整

1.添加按钮

在init的setup中添加按钮

import mammoth from "mammoth";
import { ContentTypeEnum } from '@/api/types'
import http from '@/api'

......
    // init中其余的配置[略]
    setup: function (editor: any) {
      // 注册自定义按钮
      editor.ui.registry.addButton("customUploadBtn", {
        text: "上传Word",
        onAction: function () {
          var input = document.createElement("input");
          input.type = "file";
          input.accept = ".doc,.docx";
          // 执行上传文件操作
          input.addEventListener("change", handleFileSelect, false);

          // 获取上传文件base64数据
          function arrayBufferToBase64(arrayBuffer: any) {
            var binary = "";
            var bytes = new Uint8Array(arrayBuffer);
            var len = bytes.byteLength;
            for (var i = 0; i < len; i++) {
              binary += String.fromCharCode(bytes[i]);
            }
            return window.btoa(binary);
          }

          function handleFileSelect(event) {
            var file = event.target.files[0];
            //获取上传文件后缀,如果是docx格式,则使用mammoth来进行解析,
            //如果不是则访问后台,将文件传输流base64传递到后台
            //生成文件,然后用java解析doc,并返回到前台
            var extension = file.name.slice(
              ((file.name.lastIndexOf(".") - 1) >>> 0) + 2
            );
            if (extension === "docx") {
              readFileInputEventAsArrayBuffer(event, function (arrayBuffer: any) {
                var base64Data = arrayBufferToBase64(arrayBuffer);
                console.log(base64Data);
                mammoth
                  .convertToHtml({ arrayBuffer: arrayBuffer }, { ignoreEmptyParagraphs: false, })
                  .then(displayResult, function (error: any) {
                    console.error(error);
                  });
              });
            } else if (extension === "doc") {
              let formData = new FormData()
              formData.append('file', file)
              http
                .post<string>(`/WordToHtml`, formData, {
                  headers: { 'Content-Type': ContentTypeEnum.FORM_DATA },
                })
                .then((json: any) => {
                  tinymceInstance.activeEditor.setContent(json.Data);
                })
                .catch((err) => {
                  console.log(err);
                })

              // readFileInputEventAsArrayBuffer(event, function (arrayBuffer:any) {
              //   //base64文件流
              //   var base64Data = arrayBufferToBase64(arrayBuffer);
              //   var result = "后台请求";
              //   alert(result);
              //   console.log(base64Data);
              // });
              // //tinymce的set方法将内容添加到编辑器中
              // tinymceInstance.activeEditor.setContent(result);
            }
          }

          function displayResult(result) {
            //tinymce的set方法将内容添加到编辑器中
            tinymceInstance.activeEditor.setContent(result.value);
          }

          function readFileInputEventAsArrayBuffer(event, callback) {
            var file = event.target.files[0];
            var reader = new FileReader();
            reader.onload = function (loadEvent) {
              var arrayBuffer = loadEvent.target.result;
              callback(arrayBuffer);
            };
            reader.readAsArrayBuffer(file);
          }

          // 触发点击事件,打开选择文件的对话框
          input.click();
        },
      });
    },
......

2.在按钮的栏目下显示新添加的按钮
  toolbar: () => [
    'print preview fullscreen | customUploadBtn',
  ],

3.后端接口,使用.Net写的,分别尝试了Aspose和Spire,结果是Aspose的转换效果更好

        public IActionResult WordToHtml(IFormFile file)
        {
            string fileName = file.FileName;
            string fileExtension = fileName.Substring(fileName.LastIndexOf(".") + 1);

             Document document = null;
             if (fileExtension.Equals("doc"))
             {
                 document = new Document(file.OpenReadStream(),new LoadOptions(){LoadFormat = LoadFormat.Doc});
             }
             else if (fileExtension.Equals("docx"))
             {
                 document = new Document(file.OpenReadStream(),new LoadOptions(){LoadFormat = LoadFormat.Docx});
             }
             else
             {
                 return new JsonResult(new
                 {
                     Msg = "不被支持的文档类型!"
                 });
             }
             
             HtmlSaveOptions options = new (SaveFormat.Html);
             options.ExportImagesAsBase64 = true;
             MemoryStream stream = new();
             document.Save(stream, options);
             stream.Position = 0;

             StreamReader reader = new(stream);
             string html = reader.ReadToEnd();

            // Document document = new Document();
            // if (fileExtension.Equals("doc"))
            // {
            //     document.LoadFromStream(file.OpenReadStream(),FileFormat.Doc);
            // }
            // else if (fileExtension.Equals("docx"))
            // {
            //     document.LoadFromStream(file.OpenReadStream(),FileFormat.Docx);
            // }
            // else
            // {
            //     return new JsonResult(new
            //     {
            //         Msg = "不被支持的文档类型!"
            //     });
            // }
            // document.HtmlExportOptions.ImageEmbedded = true;
            // document.HtmlExportOptions.IsExportDocumentStyles = true;
            // document.HtmlExportOptions.HasHeadersFooters = true;
            // MemoryStream stream = new();
            // document.SaveToStream(stream, FileFormat.Html);
            // stream.Position = 0;
            //
            // StreamReader reader = new(stream);
            // string html = reader.ReadToEnd();
            return new JsonResult(new
            {
                Data = html,
            });
        }