AI API 上传图片或 PDF 报 413 怎么办?请求体大小与网关限制排查
区分 HTTP 413 与上下文超限,计算 Base64 体积,使用本地脚本测量请求大小,定位网关限制并选择压缩、分批或文件引用方案。
一条短消息能正常调用模型,加入图片、PDF 或多份附件后却返回 413 Content Too Large、Payload Too Large 或 Request Entity Too Large,首先应该检查发送出去的请求体有多大。文件管理器里显示的原文件体积,不一定等于程序发送的数据体积。
本文针对上传或调用时的 HTTP 413,帮助你判断需要压缩文件、调整上传方式,还是修改自己服务器的限制。文中的数值是演示,不代表某一家中转站的实测或统一上限。
一、413 与上下文超限不是同一个问题
HTTP 413 表示某一层服务器认为请求内容超过其允许的大小。它可能发生在你的网站后端、反向代理、托管平台、中转网关或上游接口,未必已经到达模型。
| 现象 | 首先检查 | 不能直接推出的结论 |
|---|---|---|
| HTTP 413,上传大文件后出现 | 请求体字节数与沿途各层限制 | 不能据此断定模型上下文太小 |
context_length_exceeded | 输入 Token、历史消息和输出预算 | 文件小不代表 Token 少 |
| HTTP 415 或不支持格式提示 | Content-Type、文件格式、接口能力 | 单纯压缩体积不一定有用 |
| 超时或 502/504 | 上传耗时、网关超时和上游处理 | 不能只凭超时认定体积超限 |
接口可能用其他状态码表示大小错误,要结合响应正文与文档判断。如果明确提示上下文窗口不足,参照 上下文超限处理教程,不要只调整上传大小。
二、为什么 6 MiB 图片会变成约 8 MiB 请求内容
一些客户端把图片编码成 Base64,再放进 JSON 请求。标准带填充的 Base64 不含额外换行时,编码长度可以这样计算:
Base64 字符数 = 4 × ceil(原文件字节数 ÷ 3)
Base64 使用 ASCII 字符,通常比原二进制数据大约多三分之一。举例:6 MiB 文件是 6,291,456 字节,Base64 本体为 8,388,608 字节,也就是 8 MiB。再加上 data URL 前缀、JSON 字段、提示词和历史消息,整个请求会更大。
这里的 MiB 是 1,048,576 字节,MB 常指 1,000,000 字节;服务商文档可能使用不同单位,比较时先统一成字节。同一张图片也可能被历史消息重复附带,每一轮都增加体积。
PDF 则要先看客户端如何处理:直接上传二进制文件、Base64 内嵌、提取成文本,还是逐页转换为图片。小 PDF 转成多张图片后可能显著变大;纯文本提取后也可能转而触发 Token 限制。不能只看 .pdf 文件本身的大小。
三、在本地测量,不必先花钱发请求
下面脚本适用于 Node.js 20 或更新版本,保存为 check-request-size.mjs。它只读取本地文件大小,不读取文件内容、不连接网络、不调用模型。
import { stat } from 'node:fs/promises';
const [filePath, mode = 'raw'] = process.argv.slice(2);
if (!filePath || !['raw', 'base64'].includes(mode)) {
console.error('Usage: node check-request-size.mjs <file> [raw|base64]');
process.exitCode = 1;
} else {
try {
const info = await stat(filePath);
if (!info.isFile()) throw new Error('Not a regular file');
const bytes = info.size;
const estimatedBytes = mode === 'base64'
? 4 * Math.ceil(bytes / 3)
: bytes;
console.log(JSON.stringify({
fileBytes: bytes,
mode,
measuredOrEstimatedBytes: estimatedBytes,
MiB: Number((estimatedBytes / 1048576).toFixed(3)),
}, null, 2));
} catch {
console.error('Cannot inspect file; check path and permissions.');
process.exitCode = 1;
}
}
执行 node check-request-size.mjs "photo.jpg" base64 可估算图片编码本体;执行 node check-request-size.mjs "request.json" raw 可测量已经序列化保存的 JSON 文件。raw 只有在文件就是待发送请求体、发送前不再改写且没有额外传输编码时,才能代表相应的请求体大小。
不要把 Base64 估算结果当成总请求体大小。multipart 上传还包含边界和字段;压缩传输也可能分别受到压缩前或解压后大小限制。若已有 JSON.stringify(payload) 得到的字符串,可在 Node.js 中用 Buffer.byteLength(body, 'utf8') 测量,不能直接把字符串的 length 当字节数。
包含密钥、客户文件或敏感正文的请求样本应只在受控本地环境处理,用完按项目规则清理,不要上传到公开“在线测大小”工具。
四、按链路定位哪一层拒绝请求
先画出自己的实际链路,例如“浏览器 → 自己的后端 → 托管平台或代理 → 中转 API”。每一层都可能有限制;在同样的体积计算口径下,整条链路受最小允许值约束。
用时间、请求编号、网关日志和应用日志关联同一次调用。自己的后端完全没收到请求,是前置层拦截的线索,但日志缺失也可能是采集问题;不要仅凭没有日志就下结论。响应页的品牌、Server 头同样只是线索。
如果有服务商明确允许的 API 测试入口,可以用同一账户、同一模型和不敏感的小样本比较两条链路。不要为了绕过限制把生产文件或 Key 发到陌生域名,也不要反复发送超大请求寻找极限。一次最小样本、一次合理缩小后的样本,通常比盲目重试更有诊断价值。
五、优先缩小内容,再考虑提高上限
图片可以先裁掉无关背景、降低不需要的分辨率,或选择合适的有损编码。以文字识别为目标时,要检查小字是否仍清晰,压缩后能上传不代表答案质量合格。不要只改扩展名伪装文件格式。
PDF 可先只上传有关页,或分章节提取文本。需要跨页表格和版面关系时,拆分会丢失上下文,应保留页码和交接说明。分批后可能增加调用次数和总费用,需要核对账单。
若接口支持“先上传文件、再引用文件 ID”,可以避免每轮把同一文件内嵌进 JSON;但文件上传接口本身仍有大小、格式、保存期限和权限限制。如果接口支持图片 URL,发送链接可能缩小 JSON,但不能保证上游接受图片,也不能用公开链接暴露私人文件。能力与格式可先看 图片输入教程。
只有你管理的服务端才适合调整配置。例如 Nginx 的 client_max_body_size 控制其接受的请求体大小,应在相应位置设置经过评估的有限值,并检查前后各层限制。托管平台的硬性上限可能无法由项目代码更改。不要把上传上限直接设为无限:内存占用、磁盘暂存、并发、超时和费用控制都需要一起评估。
六、413 后要不要重试,如何确认修复
固定大小限制下,原样重发通常还是失败。某些临时限制可能带 Retry-After,此时结合接口文档处理,不能无限自动重试。没有拿到结果,也不等于可以保证没有扣费:尤其经过客户端自动重试、分批上传或中转转换后,要用请求编号核对调用记录。
修复后先用不含敏感信息的小样本验证,再使用缩小后的实际内容。确认请求成功、模型确实读到了附件、页码或图片数量正确、账单没有异常重复记录。不要只以 HTTP 200 作为完成标准。
可以按以下模板向客服反馈,不必直接发送客户文件:
发生时间与时区:
接口域名、路径与模型(不含密钥):
HTTP 状态及错误码:
原文件格式与字节数:
发送方式:二进制 / Base64 / 文本 / 文件 ID / URL
完整请求体大小:已测量数值,或注明未知
请求编号(如有):
小样本是否正常,缩小后是否成功:
是否经过自己的网站后端或代理:
参考资料(2026-10-07 查阅):MDN:413 Content Too Large、MDN:Base64、Nginx:client_max_body_size。这些资料说明协议和组件行为,不代表各服务商当前的上传上限。