在Telegram机器人开发中,经常需要从互联网下载一张远程图片并发送到聊天窗口,例如抓取天气信息、每日一图或用户提供的链接。很多新手在实现时容易踩坑,导致图片无法显示、超时或安全漏洞。本文将基于官方Bot API,为你详解两种主流实现方案,并给出生产环境下的最佳实践。
一、Telegram Bot API发送图片的基本原理
Telegram Bot API提供了sendPhoto方法,用于向指定聊天发送图片。该方法的photo参数支持三种类型:已上传文件的file_id、HTTP URL,以及multipart/form-data上传的文件。当传入URL时,Telegram服务器会主动从该地址下载图片并发送给用户,整个过程不需要你的服务器参与下载。这被称为“URL直传”。但有时我们需要先对图片进行处理(如裁剪、加水印),或者URL来源不稳定,这时就需要先下载到本地再上传。
二、方案一:直接使用远程URL发送图片(URL直传)
这是最简单快捷的方式,代码量最少。只需调用sendPhoto并传入photo参数为HTTP/HTTPS URL即可。以下使用Python的requests库演示。
import requests
bot_token = 'YOUR_BOT_TOKEN'
chat_id = 'YOUR_CHAT_ID'
image_url = 'https://example.com/image.jpg'
url = f'https://api.telegram.org/bot/sendPhoto'
payload = {
'chat_id': chat_id,
'photo': image_url
}
response = requests.post(url, json=payload)
print(response.json())这种方式有几个特点:
- 无需占用服务器带宽和存储,Telegram服务器负责下载。
- 要求URL必须是公网可访问的,不能被防火墙或登录拦截。
- Telegram对URL有大小和超时限制,通常图片需在5MB以内,下载超时约20秒。如果图片过大或地址响应慢,会发送失败。
- 不支持需要presigned URL或带认证头的私有资源。
三、方案二:先下载图片再发送(本地中转)
当需要处理图片、或URL不适合直接暴露给Telegram时,我们可以先将其下载到本地(或内存),再用multipart方式发送。这种方法更可控,且能更优雅地处理错误。
3.1 使用requests下载图片到内存
import requests
image_url = 'https://example.com/image.jpg'
response = requests.get(image_url, timeout=10)
response.raise_for_status() # 检查是否下载成功
image_bytes = response.content # 图片字节流3.2 将图片作为multipart上传
from requests_toolbelt import MultipartEncoder
import requests
bot_token = 'YOUR_BOT_TOKEN'
chat_id = 'YOUR_CHAT_ID'
url = f'https://api.telegram.org/bot/sendPhoto'
encoder = MultipartEncoder(
fields={
'chat_id': chat_id,
'photo': ('image.jpg', image_bytes, 'image/jpeg')
}
)
headers = {'Content-Type': encoder.content_type}
response = requests.post(url, data=encoder, headers=headers)
print(response.json())注意:requests-toolbelt库需要额外安装,也可使用requests的原生files参数,但对于少量图片完全够用:
files = {'photo': ('image.jpg', image_bytes, 'image/jpeg')}
response = requests.post(url, data={'chat_id': chat_id}, files=files)四、处理常见问题与异常
无论采用哪种方案,都需关注以下问题:
4.1 网络超时
下载远程图片时,必须设置合理的超时时间(如10秒),避免线程被卡死。建议使用requests.get(..., timeout=(connect_timeout, read_timeout))。
4.2 图片格式与大小
Telegram支持的图片格式包括JPEG、PNG、GIF(静态)等。发送前可检查MIME类型,确保符合要求。图片大小受Bot API限制,如果超过5MB,建议先压缩(如使用Pillow库)再发送。
4.3 错误处理
对于URL直传,Telegram会返回400错误,并附带描述如Bad Request: failed to get HTTP URL content。对于本地中转,需捕获下载阶段的异常(DNS解析失败、连接拒绝等)。统一封装异常处理,并记录日志,有助于定位问题。
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
except requests.exceptions.RequestException as e:
logger.error(f'下载图片失败: ')
# 可回退到其他CDN或发送错误提示五、安全最佳实践
下载远程图片存在安全隐患,必须注意:
- SSRF风险:避免让用户完全控制URL,否则可能被恶意用户利用来探测内网。建议使用白名单域名或URL验证,检查协议只允许HTTP/HTTPS,并禁止IP地址。
- 内容安全:远程下载的内容可能是恶意文件,Telegram官方会扫描病毒,但仍建议自行验证文件类型,不要盲目相信扩展名。
- 隐私保护:如果URL包含敏感信息(如token),不要将其发送给Telegram服务器,应先下载后上传。
总结
本文详细介绍了Telegram机器人下载并发送远程图片的两种方案:URL直传和本地中转。URL直传适合快速实现且资源公开的场景;本地中转则更灵活安全,便于添加处理逻辑。开发者应根据实际需求选择,同时做好异常处理和SSRF防护,以确保机器人稳定可靠。掌握这些方法,你可以灵活应对各种图片发送需求。