
一、保存触发——什么时候开始保存保存动作的触发点有两个消息回调触发实时收到图片或文件时立刻保存和定时补录触发扫描历史消息补存遗漏的素材。回调触发是主力但回调可能丢5秒超时、服务重启、网络波动所以定时补录是兜底。两种触发配合保证素材不遗漏。二、保存路径——存到哪里怎么存素材存储有三层选择临时目录下载后快速暂存定期清理、本地持久目录按日期分文件夹永久保存、对象存储OSS/S3可扩展可备份。建议方案小量素材存本地目录按日期分文件夹简单够用量大后迁移到对象存储。路径规则统一/{year}/{month}/{day}/{msgId}.{ext}按日期可浏览按 msgId 可检索。三、保存索引——怎么找到存了的素材素材存下来不等于找得到。每条素材都要建索引记录消息ID、发送人、群ID、文件名、文件类型、存储路径、文件大小、接收时间、处理状态。索引建在数据库里查询时按发送人查、按时间范围查、按文件类型查、按关键词查都能做到。没有索引的素材等于丢了——文件在硬盘上但没人知道在哪。四、保存完整性——怎么确保存对了保存完整性三个检查点下载后校验文件大小和回调里的对比、校验文件类型扩展名和实际文件头对比、校验文件可读性图片能否打开、PDF能否解析。校验失败的标记为损坏保留原始下载内容待修复不直接删除。定期扫描损坏文件列表人工排查原因。保存策略对照保存环节做什么关键设计触发什么时候保存回调实时 定时补录路径存哪里怎么存本地按日期分目录 / 对象存储索引怎么找存了的数据库索引msgId/发送人/类型/路径完整性怎么确保存对了大小校验、类型校验、可读性校验素材保存完整流程import os, hashlib def save_media(msg_id, ref, from_user, file_name, msg_type): # 下载 raw download_with_retry(ref) if not raw: db.log_media(msg_id, statusdownload_failed) return # 路径按日期分目录 today datetime.now().strftime(%Y/%m/%d) ext file_name.rsplit(., 1)[-1].lower() if . in file_name else dat path f/media/{today}/{msg_id}.{ext} os.makedirs(os.path.dirname(path), exist_okTrue) with open(path, wb) as f: f.write(raw) # 索引落库 db.save_media({ msg_id: msg_id, from_user: from_user, file_name: file_name, file_type: ext, path: path, size: len(raw), status: saved, create_time: datetime.now() }) # 完整性校验 if not verify_file(path, ext): db.update_media(msg_id, statuscorrupted)落地建议素材保存的核心是存得下来、找得到、不损坏。回调触发和定时补录缺一不可——只靠回调迟早丢数据。路径规则从第一天就设计好后期迁移成本极高。索引表必须建没有索引的文件等于不存在。媒体下载和存储接口说明参考 Eyun 开发文档实例开通见 Eyun 官网。