首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >给内容平台自动发稿:图片是怎么"贴"进富文本编辑器的

给内容平台自动发稿:图片是怎么"贴"进富文本编辑器的

原创
作者头像
用户12646891
修改于 2026-09-23 22:00:44
修改于 2026-09-23 22:00:44
650
举报

## 起因 *场景:互联网 / 后端开发,自建服务运维。* 我要把本地写好的 Markdown 稿子,自动填进某个内容平台的富文本编辑器并正式发布。纯文字还算好办,真正的难点在**插图**——编辑器没有开放的"上传图片"接口,我得想办法把本地图片塞进去,还得保证图文顺序不乱。 整套脚本跑在一个已经登录的调试实例上(通过 `--remote-debugging-port` 起,脚本只操作页面,不碰账号密码)。下面把正文写入和插图这两块拆开说。之所以要折腾这一套,是因为我手里有不少现成的 Markdown 稿子,手动一篇篇进后台复制粘贴既慢又容易漏图,攒到三篇以上就明显不划算,所以才值得花工夫把"填正文 + 贴图 + 点发布"整条链路自动化。 ## 第一回合:正文写入,得"分片粘贴" 富文本编辑器是个自绘的可编辑区(`contenteditable`)。我最先试的是直灌文本: ```js editor.innerText = mdContent; // ❌ 不生效 ``` 没用。`contenteditable` 不吃直接赋值,得模拟用户的输入动作。我一开始还试过 `execCommand('insertText', ...)`,在部分内核上偶尔能塞进去,但遇到带格式的内容就会丢结构,而且新版本浏览器已经把 `execCommand` 标成废弃,靠它不长久。所以我放弃了这条歧路,改用**分片粘贴**——把正文按段落切开,逐段投递粘贴事件,让编辑器像"用户一段段粘进来"那样接收,顺带保留 Markdown 结构: ```js function pasteBlock(el, text) { el.focus(); const dt = new DataTransfer(); dt.setData('text/plain', text); el.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true, })); } ``` 逐段调用 `pasteBlock`,比一次性塞一大坨稳,结构也不容易乱。 ## 第二回合:插图,靠"模拟粘贴"骗过上传逻辑 编辑器没有上传 API,但用户手动粘贴图片它是认的。那我就**模拟一次粘贴**:构造一个带 `File` 对象的 `ClipboardEvent` 投给编辑器,前端会当成"用户粘了一张图",走它自己的上传逻辑传到对象存储,再回填图片链接。 ```js async function pasteImage(editor, file) { editor.focus(); // 关键点2:必须聚焦 const dt = new DataTransfer(); dt.items.add(file); // 关键点1:往 clipboardData 塞 File editor.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, })); // 关键点3:上传是异步的,轮询等占位符变成真实的图片节点 await waitForImage(editor); } function waitForImage(scope, timeout = 15000) { const end = Date.now() + timeout; return new Promise((resolve, reject) => { const t = setInterval(() => { const img = scope.querySelector('img'); // 等图片节点出现 if (img) { clearInterval(t); resolve(img.src); } else if (Date.now() > end) { clearInterval(t); reject('timeout'); } }, 200); }); } ``` 这里有三处坑,后面单列。为了保证图文相对位置,我在正文里留了占位符 `<!--IMG:文件名-->`,脚本按序切正文,遇到占位符就插一张图: ```python import re def split_with_images(md): parts, pending_img = [], None for seg in re.split(r'(<!--IMG:.*?-->)', md): if seg.startswith('<!--IMG:'): pending_img = seg[7:-4] # 取出文件名 else: parts.append(('text', seg)) if pending_img: parts.append(('img', pending_img)) pending_img = None return parts ``` 正文写完一段、遇占位符就调一次 `pasteImage`,插图紧跟在对应段落后面,顺序就对了。 ## 踩到的坑 **坑一:没聚焦就投递,事件被忽略。** 粘贴事件必须在编辑器获得焦点时派发,否则前端直接丢弃。每次投之前都先 `editor.focus()`。 **坑二:不等上传完成,图文错位。** 上传是异步的,占位符要过一会儿才变成真正的图片节点。不轮询就继续往下写,后面的文字会插到图片前面,整篇顺序全乱。所以 `pasteImage` 里必须 `await waitForImage`。 **坑三:占位符切分漏了边界。** 占位符在段尾时,如果切分逻辑没把"上一段文本 + 紧跟的图片"绑在一起,图片会落到下一段。用上面的 `split_with_images` 把 img 挂到前一个 text 之后就稳了。 ## 结果 三篇稿子全部跑通,图片上传情况是 **3/3、3/3、3/3** 全部成功,正文结构校验通过,最后页面弹出"发布成功,正在审核中"。单篇整体耗时约 69 秒(含逐段粘贴 + 3 张图上传 + 复查)。 | 稿子 | 图片上传 | 正文校验 | 发布结果 | |---|---|---|---| | 第一篇 | 3/3 | 通过 | 审核中 | | 第二篇 | 3/3 | 通过 | 审核中 | | 第三篇 | 3/3 | 通过 | 审核中 | ## 四条经验 **第一,富文本自动化里"能贴就能传"。** 模拟用户粘贴,往往比翻文档找隐藏接口更稳,前端自己的上传逻辑会帮你把图送进对象存储。 **第二,粘贴事件先聚焦再派发。** 这是最容易被忽略的一步,没焦点一切白费。 **第三,异步上传必须轮询等待。** 图片不上传完就继续写,图文顺序一定乱,轮询等图片节点出现,是最省心的等法。 **第四,用占位符锁定图文位置。** 正文里写 `<!--IMG:文件名-->`,脚本按序切割,遇占位符插一张图,相对位置天然正确。比起事后在编辑器里"找到图片该插哪",预先在源文档里标记顺序,既好调试也好复查,哪张图没传上来一眼就能看出来。 **第五,整篇流程要留"复查"环节。** 三篇能 3/3 全过,靠的不只是脚本能跑,而是每贴完一段、每张图传完,都去校验一遍 DOM 真的写进去了。约 69 秒的单篇耗时里,有一小半花在复查上,但这笔时间值得——宁可慢一点,也不要发出一篇图文错位的稿子。 --- *本文记录的是实际调试过程,所用事件名与字段名均来自真实脚本,账号与站点已脱敏。* #WorkBuddy #AI办公

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档