杰瑞科技汇

这里为您生成一个符合字数要求的疑问标题,,Python requests 上传文件总失败?如何正确使用流式上传解决大文件传输难题?

下面我将从最基础的用法开始,逐步讲解更复杂的场景,并提供完整的代码示例。

这里为您生成一个符合字数要求的疑问标题,,Python requests 上传文件总失败?如何正确使用流式上传解决大文件传输难题?-图1
(图片来源网络,侵删)

核心概念

requests 上传文件主要通过 requests.post() 方法实现,关键在于 files 参数,这个参数需要一个字典,

  • :通常是后端服务器期望接收的文件字段名(<input name="..."> 中的 name 属性)。
  • :一个文件元组,格式为 (文件名, 文件对象, 内容类型)
# files 参数的基本结构
files = {
    'file_field_name': ('file_name_on_server', file_object, 'content_type')
}

content_type 是可选的,requests 通常可以自动推断。


最基础的上传:上传本地文件

这是最常见的情况,即上传你电脑上的一个文件。

代码示例:上传一个图片文件

这里为您生成一个符合字数要求的疑问标题,,Python requests 上传文件总失败?如何正确使用流式上传解决大文件传输难题?-图2
(图片来源网络,侵删)
import requests
# 目标上传URL
url = 'https://httpbin.org/post' # 这是一个测试服务器,会返回你发送的所有信息
# 1. 准备要上传的本地文件路径
file_path = 'my_image.png'
# 2. 打开文件,以二进制模式('rb')读取
#    使用 with 语句可以确保文件被正确关闭
with open(file_path, 'rb') as f:
    # 3. 构建 files 字典
    #    'file' 是后端接收文件时使用的字段名
    #    'my_image.png' 是上传到服务器后,文件在表单中显示的名称
    #    f 是文件对象
    files = {'file': ('my_image.png', f)}
    # 4. 发送 POST 请求
    response = requests.post(url, files=files)
# 5. 检查响应状态码
response.raise_for_status()  # 如果请求失败 (状态码非 200), 会抛出异常
# 6. 打印服务器返回的内容
print("上传成功!")
print("响应状态码:", response.status_code)
# httpbin.org 会返回 JSON 格式的响应,其中包含了文件信息
print("服务器返回的 JSON:")
print(response.json())

代码解析:

  1. with open(...): 强烈推荐使用 with 语句来处理文件,因为它会在代码块执行完毕后自动关闭文件,即使发生错误也不例外。
  2. 'rb': 以二进制模式读取文件,这对于图片、视频、PDF 等非文本文件至关重要,即使是文本文件,也建议使用二进制模式上传,以避免编码问题。
  3. files 参数: 我们传递了一个字典。requests 会看到这个参数,并自动将请求的 Content-Type 设置为 multipart/form-data,这是上传文件的标准格式。

上传内存中的文件(BytesIO)

如果你不需要将文件保存到磁盘,而是直接在内存中处理(从网络下载、由其他库生成等),可以使用 io.BytesIO

代码示例:

import requests
import io
url = 'https://httpbin.org/post'
# 1. 准备文件内容(这里是字符串,但可以是任何字节)
file_content_bytes = b"This is the content of my file in memory."
# 2. 创建一个 BytesIO 对象
file_object = io.BytesIO(file_content_bytes)
# 3. 构建 files 字典
#    这次我们让 requests 自动推断内容类型
files = {
    'file': ('memory_file.txt', file_object)
}
# 4. 发送请求
response = requests.post(url, files=files)
# 5. 处理响应
response.raise_for_status()
print("内存文件上传成功!")
print("响应内容:", response.json()['form'])

上传多个文件

requests 也支持在一次请求中上传多个文件,你只需要在 files 字典中为每个文件提供一个键即可。

这里为您生成一个符合字数要求的疑问标题,,Python requests 上传文件总失败?如何正确使用流式上传解决大文件传输难题?-图3
(图片来源网络,侵删)

代码示例:同时上传两个文件

import requests
url = 'https://httpbin.org/post'
# 准备两个本地文件
file1_path = 'my_image.png'
file2_path = 'my_document.pdf'
# 构建包含多个文件的 files 字典
# 'file1' 和 'file2' 是后端用来区分不同文件的字段名
files = {
    'file1': ('image.png', open(file1_path, 'rb')),
    'file2': ('document.pdf', open(file2_path, 'rb'))
}
try:
    # 发送请求
    response = requests.post(url, files=files)
    response.raise_for_status()
    print("多文件上传成功!")
    print("服务器返回的文件信息:", response.json()['files'])
finally:
    # !!!重要!!!
    # 手动关闭所有打开的文件
    for f in files.values():
        f[1].close()

注意:当你直接传递 open() 的结果时,requests 不会为你关闭文件,你必须手动关闭它们,如 finally 块所示,这也是为什么使用 with open() 更安全的原因。


携带其他数据(表单数据)一起上传

上传文件时还需要提交一些额外的文本信息,比如用户名、描述等,你可以同时使用 filesdata 参数。

data 参数用于普通的表单字段,files 参数用于文件字段。

代码示例:

import requests
url = 'https://httpbin.org/post'
file_path = 'my_image.png'
# 准备文件
with open(file_path, 'rb') as f:
    # files 用于文件
    files = {'file': ('my_image.png', f)}
    # data 用于其他表单数据(键值对)
    # 这相当于 <input type="text" name="username">
    #        <input type="text" name="description">
    data = {
        'username': 'test_user',
        'description': 'This is a test image upload.'
    }
    # 同时发送 files 和 data
    response = requests.post(url, files=files, data=data)
response.raise_for_status()
print("带数据的文件上传成功!")
# 查看表单数据和文件信息
print("表单数据:", response.json()['form'])
print("文件信息:", response.json()['files'])

高级用法与进阶

显示上传进度

requests 本身不直接支持进度条,但你可以结合 tqdm 库来实现一个非常酷炫的进度条。

安装 tqdm: pip install tqdm

代码示例:

import requests
from tqdm import tqdm
url = 'https://httpbin.org/post'
file_path = 'my_image.png' # 假设这是一个大文件
# 获取文件大小用于进度条
file_size = os.path.getsize(file_path)
# 1. 创建一个 tqdm 进度条对象
#    total=文件大小, unit='B', unit_scale=True, unit_divisor=1024
#    desc 是进度条前缀
with tqdm(
    total=file_size, 
    unit='B', 
    unit_scale=True, 
    unit_divisor=1024,
    desc=file_path
) as progress_bar:
    # 2. 定义一个回调函数,在每次数据块被发送后更新进度条
    def update_progress(chunk):
        progress_bar.update(len(chunk))
    # 3. 打开文件并设置 data 的回调
    with open(file_path, 'rb') as f:
        files = {'file': (file_path, f)}
        # 使用 data 参数的 hooks 机制
        response = requests.post(
            url, 
            files=files, 
            data={'description': 'File with progress bar'},
            hooks={'response': lambda r, *args, **kwargs: progress_bar.close()},
            # 流式传输,设置 chunksize
            stream=True
        )
response.raise_for_status()
print("带进度条的上传完成!")

自定义请求头

上传文件时,你可能需要添加自定义的 Authorization 头来验证身份。

import requests
url = 'https://your-upload-api.com/upload'
file_path = 'my_image.png'
headers = {
    'Authorization': 'Bearer YOUR_API_TOKEN'
}
with open(file_path, 'rb') as f:
    files = {'file': f}
    response = requests.post(url, files=files, headers=headers)
print(response.json())
场景 files 参数示例 data 参数示例
上传单个文件 {'file': ('name.txt', open('f.txt', 'rb'))} 不需要
上传内存文件 {'file': ('name.txt', io.BytesIO(b'data'))} 不需要
上传多个文件 {'f1': (...), 'f2': (...)} 不需要
文件 + 表单数据 {'file': (...)} {'key': 'value'}

requests 的文件上传功能非常强大且灵活,几乎可以满足所有常见的文件上传需求,记住最核心的 files 参数,并始终使用 with open() 来安全地处理文件。

分享:
扫描分享到社交APP
上一篇
下一篇