
本文介绍在 Telethon 框架下,如何通过标准化 file_id 风格的标识符统一存储和复用图片、视频等 Telegram 媒体资源,避免混用 pickle 序列化与不兼容的 file.id,提升可维护性与跨媒体类型一致性。
本文介绍在 telethon 框架下,如何通过标准化 `file_id` 风格的标识符统一存储和复用图片、视频等 telegram 媒体资源,避免混用 pickle 序列化与不兼容的 `file.id`,提升可维护性与跨媒体类型一致性。
Telegram Bot 开发中,不同媒体类型(如视频、照片、文档)在 Telethon 中的处理方式存在差异:视频消息可通过 message.file.id 直接获取稳定 file_id 并复用;而照片(Photo 对象)默认不提供 .file.id 属性,导致开发者常被迫采用 pickle 序列化整对象(如 price.pkl),这不仅体积大、安全性低,还难以跨会话/进程复用,且无法与视频逻辑对齐。
幸运的是,Telethon 支持基于 id + access_hash 的轻量级媒体引用机制——这正是官方 Bot API 中 file_id 的底层原理。我们无需依赖已弃用的 file.id 补丁(如强行 monkey-patch PhotoSize.location),而是应主动提取并封装核心元数据,实现真正统一、安全、可持久化的媒体存储方案。
✅ 推荐方案:自定义媒体标识符(Media ID)
以下函数将任意 Telegram 媒体消息(含 Photo、Video、Document 等)转换为可存储的字符串标识符,并在发送时还原为 Telethon 可识别的输入对象:
from telethon import types
def save_media(message) -> str:
"""将消息中的媒体转换为可存储的字符串标识符"""
if not message.file:
raise ValueError("Message has no media")
media = message.file.media
# 类型标记:0=photo, 1=document/video/sticker/etc.
media_type = 0 if message.photo else 1
return f"{media_type}|{media.id}|{media.access_hash}"
def load_media(identifier: str) -> types.InputPhoto | types.InputDocument:
"""从标识符还原为 Telethon 输入对象"""
parts = identifier.split('|')
if len(parts) != 3:
raise ValueError("Invalid media identifier format")
mtype, mid, mhash = map(int, parts)
if mtype == 0:
# InputPhoto: id, access_hash, file_reference (可为空字节串)
return types.InputPhoto(mid, mhash, b'')
else:
# InputDocument: 同样适用于视频、GIF、文档等
return types.InputDocument(mid, mhash, b'')
? 使用示例
保存媒体(管理员上传时):
@client.on(events.NewMessage(pattern="/save_media", from_users=ADMIN_IDS))
async def save_handler(event):
if not event.message.file:
await event.reply("请发送一张图片或一个视频。")
return
media_id = save_media(event.message)
# 存入数据库或 JSON 文件,例如:
db.set(f"media:{event.chat_id}", media_id)
await event.reply(f"✅ 媒体已保存,ID: `{media_id}`")
发送媒体(向用户推送时):
@client.on(events.NewMessage(pattern="/send_media"))
async def send_handler(event):
media_id = db.get(f"media:{event.chat_id}")
if not media_id:
await event.reply("❌ 未找到已保存的媒体。")
return
try:
input_media = load_media(media_id)
await client.send_file(
event.chat.id,
input_media,
caption="这是您请求的媒体内容"
)
except Exception as e:
await event.reply(f"⚠️ 发送失败:{e}")
⚠️ 注意事项与最佳实践
-
file_reference可省略:Bot 账户调用send_file()时,file_reference参数通常可传空字节串b'';若未来出现FILE_REFERENCE_EXPIRED错误,需捕获异常并重新获取(通过client.get_messages()或client.get_file())。 -
类型判断要严谨:
message.photo仅对纯图片消息为True;若需支持带标题的图片/视频,请改用isinstance(message.media, types.MessageMediaPhoto)或types.MessageMediaDocument进行更精确判断。 -
避免 pickle:
pickle严重依赖 Python 版本与对象结构,不可用于生产环境的长期存储或跨服务共享;而id|access_hash是 Telegram 官方协议级标识,稳定、紧凑、无依赖。 -
权限与有效期:
id+access_hash组合在账号登录态有效期内长期可用(除非原文件被删除或账号登出),但不等同于 Bot API 的永久file_id——因此建议搭配简单缓存策略(如 Redis TTL)提升可靠性。
通过该方案,你不仅能统一视频与图片的存储逻辑,还可轻松扩展至语音、动画、文件等其他媒体类型,真正实现“一套标识、多端复用”的工程目标。










