在富文本编辑器中处理图文资源
1k3Frontend2025-07-132025-08-24
背景
功能是在富文本编辑器创建/编辑一些图文内容, 图文图文, “文”没什么需要操心的, 主要是“图” 。
图乍一看好像也没什么担心的, 但是用户行为大概率不会按照期望去做。, 有图片上传按钮,但是用户可能会粘贴或者拖拽,刚开始暂时都还能显示, 只是个别会因为跨域或者验证限制什么的会无法显示。但这从外部来的 src 链接不是自己服务器的, 指不定什么时候就失效了。所以理想情况还是要下载存储到服务器。
- 富文本编辑器是基于
wangeditor
, 用户添加图片的行为主要是- 菜单栏上传按钮, 合法流程, 没什么操心的
- 复制粘贴图片, 截图然后粘贴, 或者从文件夹拖拽图片进编辑器, 这几个也算合法流程, 因为数据类型都包含
Files
会触发editor
的上传事件, 就走到了合法流程。如果用的 editor 没有触发, 也可以通过监听paste
/drop
事件来监听- tips
:wangeditor
监听了drop
事件而且大概是把事件挂在了里面的元素上, 而不是外层的 container, 而 event 默认是冒泡, 从里到外传递。而我们已知的元素是 container, 如果把drop
事件挂在container
上,editor
会成先一步完成。所以设置drop
事件的时候将模式修改为使用捕获模式, 从外到里, 才能在 container 上拦截drop
事件
- tips
- 选中图文, 复制粘贴图片或者拖拽进编辑器。这个
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
5const 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
4const 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