2026-03-10
Python
00

目录

背景:消失的文件
1. 问题的根源:RFC 2231 协议冲突
2. 核心思路:Monkey Patch(猴子补丁)
3. 解决方案代码
4. 为什么这段代码有效?
5. 总结

背景:消失的文件

在使用 Python 的 requests 库上传文件时,如果文件名包含中文,你可能会遇到一个诡异的问题:

  • 本地调试:代码运行正常,没有任何报错。
  • 服务端表现:对方声称根本没有收到文件(File 字段为空),或者收到的文件名是一串乱码(如 filename*=utf-8''...)。

即便你尝试了 encode('utf-8').decode('latin-1') 等常规编码转换,问题依然可能存在。

1. 问题的根源:RFC 2231 协议冲突

现代版本的 requests(及其底层的 urllib3)为了遵循 RFC 2231 标准,在发现文件名包含非 ASCII 字符(如中文)时,会自动将 Content-Disposition 里的 filename 参数转换成如下格式:

HTTP
Content-Disposition: form-data; name="file"; filename*=utf-8''%E4%B8%AD%E6%96%87.xlsx

然而,很多现有的后端框架(如旧版 Spring、PHP 或某些 Nginx 配置)并不支持 filename*= 这种语法。它们只认最原始的:

HTTP
Content-Disposition: form-data; name="file"; filename="中文.xlsx"

由于识别不到 filename 这个 Key,服务器会直接丢弃该文件部分,导致报错或获取不到文件。

2. 核心思路:Monkey Patch(猴子补丁)

既然 urllib3 内部强制执行了新标准,而我们又无法轻易修改第三方库的源代码,最好的办法就是通过 Monkey Patch,在程序运行时动态替换 urllib3 格式化 Header 参数的函数。

3. 解决方案代码

在你的请求脚本最上方(import requests 之后),加入以下代码:

Python
import urllib3.fields def format_multipart_header_param(name: str, value) -> str: """ 自定义格式化函数: 1. 强制不使用 RFC 2231 的 filename*= 语法。 2. 保持原生的 filename="xxx" 格式。 3. 对特殊字符 (\n, \r, ") 进行转义处理。 """ if isinstance(value, bytes): value = value.decode("utf-8") # 对文件名中的换行符、回车符和双引号进行百分号编码,防止 Header 解析截断 # 10 -> \n, 13 -> \r, 34 -> " value = value.translate({10: "%0A", 13: "%0D", 34: "%22"}) return f'{name}="{value}"' def patched_format_header_param(name: str, value) -> str: return format_multipart_header_param(name, value) # 核心:替换 urllib3 内部字段的处理函数 urllib3.fields.format_header_param = patched_format_header_param # --- 之后正常使用 requests 即可 --- import requests files = {'file': ('2026年3月生日礼.xlsx', open('test.xlsx', 'rb'))} response = requests.post("http://example.com/upload", files=files)

4. 为什么这段代码有效?

  1. 屏蔽自动转换:原本的 urllib3.fields.format_header_param 会检查字符是否安全,如果不安全则启用 filename*。我们的补丁直接跳过了这个逻辑。
  2. 特殊字符处理:通过 .translate() 手动处理了文件名中可能存在的引号和换行符。这是为了防止文件名里万一有个双引号,导致生成的 Header 变成 filename="my"file".xlsx" 从而引发协议解析错误。
  3. 兼容性最强:直接发送 UTF-8 编码的原始中文字符串。在大多数现代 Web 服务器中,这种“不标准但通用”的写法反而拥有最好的兼容性。

5. 总结

在处理 HTTP 协议相关的坑时,有时候“标准”反而是障碍。当后端服务器比较传统时,通过 Monkey Patch 强行回归到简单直接的 Header 格式往往是最有效的手段。

注意: 此方案适用于 requests 2.x 系列。如果在极个别环境下仍然乱码,请确认服务器端的默认解码字符集(如 GBK 或 UTF-8)。

本文作者:Dewar

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!