Telegram Bot调用sendDocument使用Amazon S3 URL失败的原因及解决方法
问题
我正在使用PHP结合Guzzle,通过Telegram Bot API的sendDocument接口发送文档。文件托管在Amazon S3上,URL示例如下:
https://telegramfile.xxx.amazonaws.com/file/preview/chat/02AAE430F214770D1534C6364813C1FC.txt
我尝试通过以下代码发送:
$response = $client->post( "https://api.telegram.org/bot$botToken/sendDocument", [ 'json' => [ 'chat_id' => $chatId, 'document' => $fileUrl ] ] );
请求返回错误:
{ "ok": false, "error_code": 400, "description": "Bad Request: wrong type of the web page content" }
已排查内容:
- S3 URL为公开可访问状态(已通过curl验证);
- 文件Content-Type为application/octet-stream;
- 文件大小小于50MB。
使用其他公开URL(如GitHub原始文件)通过相同代码可正常发送。
问题: Telegram为何拒绝该S3 URL?如何正确通过sendDocument发送S3托管的文件?
回答
拒绝原因
Telegram Bot API对外部文件URL的校验逻辑比较严格,你的S3 URL被拒绝大概率是以下原因之一:
- 预览URL的响应结构问题:URL中的
preview路径可能让S3返回的内容带有隐性的页面包装(比如某些场景下S3预览链接会返回带预览界面的HTML,而非纯文件流,即使curl能直接获取文件); - 响应头不符合预期:虽然你设置了
application/octet-stream,但如果缺少Content-Disposition: attachment这类头,Telegram可能会把文件识别为网页内容; - 域名信任机制:Telegram的爬虫可能对部分云存储域名有特殊检测,导致URL被误判为不安全或非文件资源。
解决方案
方案1:更换为S3直接文件URL
如果你的文件有不带preview的直接访问URL,替换后重试。同时在S3控制台修改文件元数据,添加Content-Disposition: attachment; filename="你的文件名.txt",强制告知Telegram这是可下载的文件。
方案2:用multipart/form-data上传(更可靠)
绕过URL检测,直接将文件内容以表单数据的形式发送,这是Telegram API推荐的文件上传方式,兼容性更好。
方式A:借助AWS SDK读取S3文件
use Aws\S3\S3Client; // 初始化S3客户端 $s3 = new S3Client([ 'version' => 'latest', 'region' => '你的S3区域', ]); // 从S3获取文件流 $fileObject = $s3->getObject([ 'Bucket' => '你的Bucket名称', 'Key' => 'file/preview/chat/02AAE430F214770D1534C6364813C1FC.txt' ]); $fileStream = $fileObject['Body']; // 发送Telegram请求 $response = $client->post( "https://api.telegram.org/bot$botToken/sendDocument", [ 'multipart' => [ ['name' => 'chat_id', 'contents' => $chatId], [ 'name' => 'document', 'contents' => $fileStream, 'filename' => '自定义文件名.txt' // 必须指定,否则Telegram可能无法识别类型 ] ] ] );
方式B:直接通过Guzzle拉取S3文件并上传
如果不想引入AWS SDK,可以直接用Guzzle获取S3文件流再发送:
// 获取S3文件的流响应 $s3FileResponse = $client->get($fileUrl); $fileStream = $s3FileResponse->getBody(); // 发送multipart请求 $response = $client->post( "https://api.telegram.org/bot$botToken/sendDocument", [ 'multipart' => [ ['name' => 'chat_id', 'contents' => $chatId], [ 'name' => 'document', 'contents' => $fileStream, 'filename' => '自定义文件名.txt' ] ] ] );
注意事项
- 确保S3文件权限正确:如果是私有Bucket,需要生成预签名URL或者让服务器有访问权限;
- 文件名参数
filename要和文件类型匹配(比如.txt对应文本文件),否则可能出现文件类型识别错误; - 大文件建议使用流式上传,避免内存占用过高。
内容的提问来源于stack exchange,提问作者李西安子
相关产品推荐
相关产品推荐

