DSH 里文件“存不进”对象存储的原因与做法
① 什么情况下会撞上它
在 DSH 对话里让助手把本地 .xlsx 存到对象存储,结果助手回“已处理”但桶里没文件;或投递接口返回成功、控制台无新对象;或文件出现但 0 字节、内容截断。
共同点:“把文件存进去”在 DSH 里是两段链路——先把文件交到投递通道,再由通道送到外部存储。任何一段没配好,表现都是“看起来做了,实际没落地”。
② 为什么会这样
DSH 的本地投递接口是给文本消息设计的,不是文件传输接口:
POST http://127.0.0.1:3080/api/dsh-im/delivery/messages
Content-Type: application/json
{
"botId": "default",
"targetId": "ops-channel",
"text": "季度报表已生成,路径见附件说明",
"format": "plain"
}
format 只接受 plain 或 markdown,返回 {"sent": true} 只代表消息被受理,不代表文件进了对象存储。把 Excel 二进制塞进 text,或指望这个接口上传,都会“受理成功但桶里没东西”。
决定消息往哪投的是 里的 deliveryTargets 数组。targetId 必须能在其中找到对应项,否则消息被丢弃或落到默认目标。
正确心智模型:DSH 只负责通知“文件已就绪、位置在哪”;文件本体要么由脚本直接调对象存储 SDK 上传,要么由接收端按消息里的路径去取。 混淆这两件事,就是绝大多数“存不进去”的根源。
③ 怎么做
第一步,确认投递目标存在。打开 workspaces.json,检查 deliveryTargets 里是否有你要用的 targetId,没有就先补一项。
第二步,用脚本完成真正的上传。凭据从环境变量读取:
import os
from qcloud_cos import CosConfig, CosS3Client
config = CosConfig(
Region=os.environ["COS_REGION"],
SecretId=os.environ["COS_SECRET_ID"],
SecretKey=os.environ["COS_SECRET_KEY"],
)
client = CosS3Client(config)
client.upload_file(
Bucket=os.environ["COS_BUCKET"],
Key="reports/2024-q4.xlsx",
LocalFilePath="./2024-q4.xlsx",
PartSize=1,
MAXThread=4,
)
第三步,上传成功后再发通知,把对象键写进正文:
curl -s -X POST http://127.0.0.1:3080/api/dsh-im/delivery/messages \
-H 'Content-Type: application/json' \
-d '{"botId":"default","targetId":"ops-channel","text":"已上传 reports/2024-q4.xlsx","format":"plain"}'
顺序不能反:先上传、后通知。反过来接收端按消息去取时对象还不存在。
定时生成并投递用 DSH 的 schedule 能力建立定时任务,固定时刻把提示词投给会话;DSH 重启后会补发最近一次。
④ 怎么确认成了 / 哪一步失败
- 上传段:
upload_file无异常返回,控制台能看到对应Key,大小与本地一致。报AccessDenied是密钥或桶策略问题;报NoSuchBucket是Bucket名或Region写错。 - 投递段:接口返回
{"sent": true}。其他返回说明请求体字段不合法,最常见是format传了非法值,或targetId不在deliveryTargets里。 - 会话段:会话记录在
下的/sessions/ session.v4.jsonl.zstd,解压后按targetId检索,能看到即视为送达。
三段都过而对方仍说“没收到”,问题在接收端,不在 DSH。
⑤ 边界
不适合超大文件的高频同步——投递接口是消息通道,不是数据面,搬二进制很快撞上体积和编码限制。也不适合断点续传、分片校验,那些直接走对象存储的分片上传接口。
副作用两个:消息里出现对象键,等于把存储路径暴露给通道内所有人,敏感桶要单独规划 Key 前缀;定时任务重启补发只补最近一次,跨天不会逐条重放,依赖“每天都有一条”的下游逻辑要自己做幂等。