TONG-H

在富文本编辑器中处理图文资源

1k3Frontend2025-07-132025-08-24

背景

功能是在富文本编辑器创建/编辑一些图文内容, 图文图文, “文”没什么需要操心的, 主要是“图” 。

图乍一看好像也没什么担心的, 但是用户行为大概率不会按照期望去做。, 有图片上传按钮,但是用户可能会粘贴或者拖拽,刚开始暂时都还能显示, 只是个别会因为跨域或者验证限制什么的会无法显示。但这从外部来的 src 链接不是自己服务器的, 指不定什么时候就失效了。所以理想情况还是要下载存储到服务器。

  • 富文本编辑器是基于 wangeditor, 用户添加图片的行为主要是
    • 菜单栏上传按钮, 合法流程, 没什么操心的
    • 复制粘贴图片, 截图然后粘贴, 或者从文件夹拖拽图片进编辑器, 这几个也算合法流程, 因为数据类型都包含 Files 会触发 editor 的上传事件, 就走到了合法流程。如果用的 editor 没有触发, 也可以通过监听 paste / drop 事件来监听
      • tips:wangeditor 监听了 drop 事件而且大概是把事件挂在了里面的元素上, 而不是外层的 container, 而 event 默认是冒泡, 从里到外传递。而我们已知的元素是 container, 如果把 drop 事件挂在 container 上, editor 会成先一步完成。所以设置 drop 事件的时候将模式修改为使用捕获模式, 从外到里, 才能在 container 上拦截 drop 事件
    • 选中图文, 复制粘贴图片或者拖拽进编辑器。这个 types 可能包含 text/html, 也有可能包含复制的时候被设置的自定义的类型, 这里面的资源就需要自己解析处理了

图文粘贴

可能包含的图片资源

  • Base64 encoded images

    • 可以不作处理, 当做 html 的一部分保存
  • SVG files (potential XSS risk)

    • 这个在被 wangeditor 转化为普通标签, 也就是会不显示
    • 如果需要显示, 考虑到可能包含 xss 攻击, 可以通过转 image src, 再放到 canvas, 最后转化成图片(可以参考这篇文章使用 canvas 绘制 dom)
  • http 开头的远程链接

    • 创建临时 div 获取 img 标签

    • 遍历 img 标签, 筛选校验符合要求的资源。这一步就只能做筛选图片地址, 限制图片数量

      1
      2
      3
      4
      5
      const container = document.createElement('div');
      container.innerHTML = html;
      const imgNodes = Array.from(container.querySelectorAll('img')).filter(i => {
      /** 筛选 */
      })
    • 下载 => response => blob => 上传 => 替换 src

      • 文件类型可以从 content type 获取, 图片名称也可以自己生成, 尽量不要从图片链接中提取信息。如果需要从链接获取信息, 做好字符过滤
      • 在下载 body 之前, 可以先校验格式和尺寸, 避免浪费时间
        • awita fetch 阶段, 浏览器只下载 response headers and status``, body 会在 response.blob(), response.arrayBuffer(), response.text(), or response.json() 调用的时候被下载
          1
          2
          3
          4
          const response = await fetch(src);
          const mimeType = response.headers.get('content-type');
          const contentLength = response.headers.get('content-length');
          const blob = await response.blob();
      • 可能会因为对方网站有授权或者跨域保护之类的, 可能会下载失败, 或者我们自己的校验没能通过之类, 种种原因如果没能上传那就删除该图片, 或者用一个默认图片代替, 让用户知道哪个失败了

同样是复制一堆 html 标签, html or plain text 是怎么决定的?

  • 复制的文本的类型是有内容来源应用决定的, 如果是从 vscode 复制, 那么就是由 vscode 决定文本类型
  • Plain Text (text/plain) 纯字符串, 没有任何格式
  • HTML (text/html), 老伙伴了,可能包含 xss 攻击
  • Rich Text (text/rtf) 被很多编辑器(Microsoft Word, WordPad)使用的跨平台格式, 和 html 相比, 不支持多媒体资源, 交互功能不强
  • Custom Formats, 自定义的类型, 比如 vscode 就自定义了自己的类型 vscode-editor-data