
本文讲解如何在 Python 中安全解析 Content-Disposition 响应头中的国际化文件名(如日文、中文),避免因编码不匹配导致乱码(如 é³ã®ãªãã¬ããªã«),并提供兼容 RFC 5987 的健壮解决方案。
本文讲解如何在 python 中安全解析 `content-disposition` 响应头中的国际化文件名(如日文、中文),避免因编码不匹配导致乱码(如 `é³ã®ãªãã¬ããªã«`),并提供兼容 rfc 5987 的健壮解决方案。
HTTP 响应头 Content-Disposition 中的 filename 字段仅支持 ASCII,若服务端直接将 UTF-8 字符串写入 filename="音のないレプリカ.zip",实际会以 UTF-8 字节序列被当作 Latin-1(即 iso-8859-1)解析——这正是你看到 é³ã®ãªãã¬ããªã« 这类乱码的根本原因:Python 将 UTF-8 编码的字节错误地用 latin-1 解码为了字符串。
✅ 正确做法:优先解析 filename*(RFC 5987)
现代 Web 服务器应使用 filename* 字段传递编码后的文件名,例如:
Content-Disposition: attachment; filename="fallback.zip"; filename*=UTF-8''%E9%9F%B3%E3%81%AE%E3%81%AA%E3%81%84%E3%83%AC%E3%83%97%E3%83%AA%E3%82%AB.zip
该字段明确声明编码(UTF-8'')并采用百分号编码(URL encoding),可无歧义还原。
以下是增强版 download_zip 函数,自动优先解析 filename*,回退到 filename 并尝试修复常见编码错误:
import re
import requests
import urllib.parse
from typing import Optional
def download_zip(key: str):
response = requests.get('some url')
if response.status_code != 200:
print(f"Failed: {key} -> {response.status_code}")
return
header = response.headers.get('content-disposition', '')
file_name = extract_filename_from_header(header) or f"{key}.zip"
# 确保文件名合法(移除路径遍历与控制字符)
file_name = re.sub(r'[\/*?:"|]', '_', file_name)
file_name = file_name.strip()
with open(file_name, mode='wb') as f:
f.write(response.content)
print(f"Written: {file_name}")
def extract_filename_from_header(header: str) -> Optional[str]:
# 1. 优先匹配 filename* (RFC 5987)
match = re.search(r"filename*=(?:([^']*)'[^']*')?([^;]+)", header)
if match:
encoding = match.group(1) or 'utf-8'
encoded_value = match.group(2).strip('"' ')
try:
return urllib.parse.unquote(encoded_value, encoding=encoding)
except (UnicodeDecodeError, LookupError):
pass # 回退到 filename
# 2. 回退匹配 filename,并尝试修复常见编码错误
match = re.search(r'filename=(.+)', header)
if match:
raw_name = match.group(1).strip('"' ')
# 尝试:将原始字节按 latin-1 编码,再以 utf-8 解码(常见错误场景)
try:
return raw_name.encode('latin-1').decode('utf-8')
except (UnicodeDecodeError, UnicodeEncodeError):
pass
# 若失败,保留原始字符串(至少不崩溃)
return raw_name
return None
⚠️ 注意事项与最佳实践
- 服务端责任:理想情况下,后端应严格遵循 RFC 5987,设置 filename* 字段。请向 API 提供方反馈此问题。
- 文件系统兼容性:Windows/macOS/Linux 对 Unicode 文件名支持良好,但需确保 Python 运行环境的 locale 支持 UTF-8(通常默认满足)。
- 安全性:示例中已过滤路径遍历字符(.., /, )和非法文件名字符,生产环境建议进一步校验长度与空字符。
- 调试技巧:打印原始 header 和 raw_name.encode('latin-1') 的十六进制,可快速验证是否为 UTF-8 → latin-1 错误解码。
通过优先解析标准化的 filename* 字段,并辅以合理的回退策略,即可稳定支持日文、中文等任意 Unicode 文件名,彻底告别乱码问题。











