You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,提问作者李西安子

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 04:42:41