Skip to content

Requests - 文件上传

requests 库使通过 HTTP POST 请求上传文件变得非常简单。这对于与需要提交文件的 Web 应用程序交互至关重要,例如上传图片、文档或数据文件。本章将介绍如何上传单个和多个文件,以及如何自定义文件上传。

我们将使用 https://httpbin.org/post 作为测试端点,它会回显 POST 请求的详细信息,包括上传的文件。

要上传文件,你可以将一个字典传递给 requests.post() 的 files 参数。字典的键是服务器预期的字段名,值是类似文件的对象。

上传文件时,在二进制模式 ('rb') 下打开文件至关重要。使用 with 语句(上下文管理器)可以确保请求完成后文件自动关闭,这是一种最佳实践。

首先,在你的项目目录中创建一个名为 test.txt 的示例文件,并添加一些内容:

This is a test file for demonstrating file uploads with Python Requests.

现在,让我们编写 Python 代码来上传它:

import requests
url = 'https://httpbin.org/post'
file_path = 'test.txt'
# 最佳实践:使用上下文管理器来处理文件
try:
with open(file_path, 'rb') as f:
files_to_upload = {'file': f} # 'file' 是字段名
response = requests.post(url, files=files_to_upload)
print(f"Status Code: {response.status_code}")
# httpbin.org/post 返回一个包含上传详情的 JSON 响应
response_data = response.json()
print("上传的文件内容(来自服务器响应):")
print(response_data['files']['file'])
except FileNotFoundError:
print(f"错误:文件 {file_path} 未找到。")
except requests.exceptions.RequestException as e:
print(f"发生错误:{e}")
Status Code: 200
Uploaded file content (from server response):
This is a test file for demonstrating file uploads with Python Requests.

在来自 httpbin.org 的响应中,files 字段将包含上传文件的内容,从而确认上传成功。

你可以通过提供一个元组(而不是仅仅是文件对象)来为上传的文件自定义文件名、内容类型和添加自定义头部信息 (custom headers)。

示例:指定文件名、内容类型和自定义头部信息

Section titled “示例:指定文件名、内容类型和自定义头部信息”
import requests
url = 'https://httpbin.org/post'
file_path = 'test.txt' # 假设 test.txt 存在
# 元组格式:(文件名, 文件对象, 内容类型, 自定义头部)
# 内容类型和自定义头部是可选的。
try:
with open(file_path, 'rb') as f:
files_spec = {
'document': ('report.txt', f, 'text/plain', {'X-Custom-Info': 'MyReportV1'})
}
response = requests.post(url, files=files_spec)
print(f"Status Code: {response.status_code}")
response_data = response.json()
print("服务器响应(files 部分):")
print(response_data['files'])
print("服务器响应(headers 部分 - 查看 Content-Disposition):")
# 实际的多部分头部很复杂,httpbin 显示的是解析后的文件部分。
# 要查看 requests 发送的 Content-Disposition 头部,你需要检查原始请求或使用 Wireshark 等工具。
# httpbin 在键 'document' 下显示文件内容。
except FileNotFoundError:
print(f"错误:文件 {file_path} 未找到。")
except requests.exceptions.RequestException as e:
print(f"发生错误:{e}")

输出(示例,重点关注响应中的 ‘files’ 部分)

Section titled “输出(示例,重点关注响应中的 ‘files’ 部分)”
Status Code: 200
Server Response (files part):
{'document': 'This is a test file for demonstrating file uploads with Python Requests.'}
Server Response (headers part - to see Content-Disposition):
...

在这种情况下,服务器将看到使用字段名 document、文件名 report.txt、内容类型 text/plain 以及与此多部分 (multipart) 请求部分关联的自定义头部 X-Custom-Info 上传的文件。

要在单个请求中上传多个文件,只需向 files 字典添加更多条目即可。每个键值对代表一个要上传的文件。

假设你有一个 file1.txt 和一个 image.png。

import requests
url = 'https://httpbin.org/post'
# 如果文件不存在,为示例创建虚拟文件
with open('file1.txt', 'w') as f1: f1.write('Content of file1')
with open('image.png', 'wb') as f2: f2.write(b'\x89PNG\r\n\x1a\n\0\0\0\rIHDR...') # 虚拟 PNG 数据
files_to_upload = []
try:
# 准备文件列表以便使用上下文管理器打开
# (表单中的文件名, 磁盘上的实际文件名, 如果需要的内容类型)
file_specs = [
('text_file', 'file1.txt', 'text/plain'),
('image_file', 'image.png', 'image/png')
]
# 构建用于 requests 的 files 字典
# 需要保持文件对象打开直到请求发送
opened_files = [] # 用于存放需要关闭的文件对象(如果不为每个文件使用 with 语句)
files_dict_for_requests = {}
for form_name, disk_filename, content_type in file_specs:
f_obj = open(disk_filename, 'rb')
opened_files.append(f_obj)
files_dict_for_requests[form_name] = (disk_filename, f_obj, content_type)
response = requests.post(url, files=files_dict_for_requests)
print(f"Status Code: {response.status_code}")
response_data = response.json()
print("上传的文件(来自服务器响应):")
print(response_data['files'])
finally:
for f_obj in opened_files:
f_obj.close()

输出(示例,重点关注 ‘files’ 部分)

Section titled “输出(示例,重点关注 ‘files’ 部分)”
Status Code: 200
Uploaded files (from server response):
{
"image_file": "data:image/png;base64,...",
"text_file": "Content of file1"
}

注意:httpbin.org 可能会将二进制文件内容作为 base64 编码的 数据 URI (data URI) 返回。重要的是响应的 files 部分中同时存在 image_file 和 text_file 键,这表明它们已被服务器接收。

你不必总是从磁盘文件读取内容。你也可以直接发送字符串或字节内容,就像它们是文件一样:

import requests
url = 'https://httpbin.org/post'
files = {'file': ('custom_filename.txt', 'Hello, this is in-memory content!')}
response = requests.post(url, files=files)
print(f"Status Code: {response.status_code}")
response_data = response.json()
print(response_data['files']['file'])
Status Code: 200
Hello, this is in-memory content!

文件上传在 Web 应用程序中非常常见,用于实现以下功能:用户头像更新、提交报告或文档、向消息附加文件,或上传数据集进行处理等。

始终参考你正在交互的服务的 API 文档,以了解文件上传所需的字段名、文件类型和大小限制。