Requests 加文件上传后报 int 没有 translate,检查字段名
Requests 的表单没有文件时能准备,添加 files 后却报整数没有 translate。检查两组字段名,按接收端契约提供字符串,保留重复字段和冲突检查。
Requests 原来只提交 data 能正常准备请求,增加 files 后却报 AttributeError: 'int' object has no attribute 'translate'。先检查表单和文件的字段名,尤其是把数字 ID 直接当成字典键的地方;异常可能在请求发出之前就发生了。
Requests 2.34.2、urllib3 2.8.0 有这条明确触发路径。普通 URL 编码会把部分非字符串键转成文本,multipart 路径却会把文件字段名直接交给底层编码器。后者需要字符串,整数没有编码器调用的 translate()。不能因为无文件时“能用”,就把整数键看成文件上传接口承诺支持的输入。
名称与值分开检查
下面的代码只准备请求,不发送网络流量。文件字段名是 upload,上传文件名是 sample.txt,它们是两个不同的参数:
import requests
prepared = requests.Request(
"POST",
"https://example.invalid/upload",
data={"record_id": "1"},
files={"upload": ("sample.txt", b"example", "text/plain")},
).prepare()
example.invalid 是不用于实际请求的占位主机。真正的字段名与值应遵循接收端契约,不要把业务原来需要的 record_id 随手换成字符串 "1"。如果接口确实定义了数字形式的字段名,则由应用明确提供 "1"。
同时检查 data 和 files。把文件键改成字符串,却让 data={1: "a"} 继续进入 multipart,仍可能在字段名编码时失败。文件内容为字节并不要求文件字段名也是字节;名称和内容的类型用途不同。
在输入边界拒绝不符合契约的键
对自己维护的上传封装,可以先检查字段名,避免底层错误远离业务入口:
def require_string_names(fields):
for name in fields:
if not isinstance(name, str):
raise TypeError("multipart field names must be strings")
这段适用于字典或其他按键迭代的映射。若封装接受二元组列表,应检查每对数据的第一项;不要直接套用后把整个元组当名称。合法的重复字段也应保留,不能为了验证键而把列表强制转成字典。
不要无条件给任意对象做 str()。例如整数 1 与字符串 "1" 会变成同名字段,可能丢失原本独立的数据;函数、元组或 None 也不一定有接收端认可的名称。只有业务已经定义转换规则,才按该规则生成字符串并检查冲突。
截至 2026 年 10 月 26 日,正式 Requests 2.34.2 仍保留上述编码路径。应用应主动给出合法名称,而不是等待底层自动推断。
验收先确认同一输入能完成 prepare(),multipart 中的字段名、重复字段和文件名符合约定。随后才通过原有的获准测试接口检查接收端实际解析结果,并保留原超时、认证和 TLS 设置。不要打印带凭据的完整请求头或上传正文,也不要因本地准备失败反复重放原来的生产写请求。


