Mxsleon Blog

Back

multipart/form-data

常用于文件上传,其中,post​请求中的参数为files

以下是参数详细解释:

1. 代码整体

其中核心代码为,获取文件对象,拼接files参数:

with open(file_path, 'rb') as f:

    files = {
        'file': (
            Path(file_path).name,
            f,
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        ),
    }

    response = requests.post(
        url,
        headers=headers,
        files=files,
    )

整个过程可以拆分为:

open 打开文件

⬇️

得到文件对象 f

⬇️

构造 files 字典

⬇️

requests 读取文件内容

⬇️

生成 multipart/form-data

⬇️

发送 http 请求

2. ​with open(...,'rb') as f到底是什么?

其中:

f = open(file_path,'rb')

这里的 f不是文件内容

这里的区别需要额外注意!!

f实际上是:

文件对象(File Object)​ 或者说叫 文件句柄(File Handle)

打印可以得到:

f = open('123.xlsx', 'rb')
print(f.__class__)  # <class '_io.BufferedReader'>
print(type(f))      # <class '_io.BufferedReader'>
print(f)            # <_io.BufferedReader name='123.xlsx'>
f.close()           # 代码中 with 可以自动关闭文件,这里需要手动关闭

它本质上是一个 python对象

2.1 文件对象里面保存了什么?

可以想象成:

f 
|-- 文件路径
|-- 打开模式(rb)
|-- 当前读取职位
|-- 操作系统文件描述符
|-- read() 方法

等等,

例如:

文件名:f.name​ 得到:report.xlsx​,注意,当open里面的文件是绝对路径时,f​的name方法也返回绝对路径

当前光标:f.tell()​ 得到 0

读取:content = f.read(10)​ 读取 前10个字节

手动打开需要手动关闭:

f.close()

所以,f​不是Excel 文件​ 而是“如何读取 Excel 文件”的对象

3. 为什么要用 "rb" ?

在代码中:

open(file_path,'rb') 

这里:

r​表示:read,只读;

b​表示:binary,二进制模式。

Excel文件里面实际上长这样:

50 4B 03 04 ...

而不是:

姓名:张三 ...

所以上传:

  • Excel
  • PDF
  • 图片
  • ZIP

都必须使用二进制读取,rb

否则会导致:UnicodeDecodeError 或者 文件损坏。

4. ​files 到底是什么?

在代码中:

files = {
    'file': (
        Path(file_path).name,
        f,
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    )
}

它并不是普通参数。

它专门表示:

multipart/form-data

也就是:

<form enctype="multipart/form-data">

表单数据;

requests 支持三种写法:

  1. 写法一
files = {
    'file' : open('a.xlsx','rb')
}
  1. 写法二,指定文件名
files = {
    'file' : (
            '报表.xlsx',
            open('a.xlsx','rb'),
            )
}
  1. 写法三,完整写法
files = {
    'file' : (
            '报表.xlsx',
            open('a.xlsx','rb'),
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )
}

即:file​ : 文件名​,文件对象​,MIME 类型

5. 这个三元组到底什么意思?

代码中:

(
    Path(file_path).name,
    f,
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)

5.1 第一项:文件名

Path(file_path).name

假设:

file_path = "/tmp/report.xlsx"

得到:report.xlsx

服务器收到:filename = "report.xlsx"

5.2 第二项:文件对象

f

requests内部:f.read()

读取内部真实数据。

5.3 第三项:MIME 类型

告诉服务器:

这是什么文件。

代码中:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

表示:xlsx

常见的MIME 类型:

文件 MIME
txt text/plain
json application/json
jpg image/jpeg
png image/png
pdf application/pdf
xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

6. requests 最终发送的 HTTP 长什么样?

代码中:

files = {
    'file' : ( ... )
}

会被 requests 自动转换成:

POST /upload HTTP/1.1

Content-Type:
multipart/form-data;
boundary=----abcd123

请求体:

------abc123

Content-Disposition:
form-data;
name="file";
filename="report.xlsx"

Content-Type:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

<Excel二进制数据>

------abc123--

7. 为什么字段名是"file"?

这一句:

files = {
    'file': ...
}

里面:'file'

非常重要,他并不是随便写的,需要和后端保持一致,

例如 Django中:

request.FILES['file']

那么前端就必须传入相同的字段名:

files = {
    ‘file': ...
}

如果 Django

request.FILES['avatar']

那么,python:

files = {
    'avatar': ...
}

8. request 内部到底做了什么?

实际上内部流程是:

files
    ↓
requests.models.RequestEncodingMixin
    ↓
_encode_files()
    ↓
urllib3.filepost.encode_multipart_formdata()
    ↓
生成 boundary
    ↓
读取 f.read()
    ↓
拼接 HTTP
    ↓
socket.send()

最终流程:

requests
    ↓
urllib3
    ↓
http.client
    ↓
socket
    ↓
TCP

发送到服务器

9. 最容易犯的错误

9.1 错误 1

不要:

files = {
    'file': file_path
}

因为:

字符串 ≠ 文件

9.2 错误 2

不要:

open(file_path,'r')

读取模式必须:rb,原因上面已经解释

9.3 错误 3

不要自己写:

headers = {
    'Content-Type':
        "multipart/form-data"
}

因为,requests会自动生成:

boundary=xxxxxx

如果你自己写:

multipart/form-data

没有 boundary ,服务器会返回错误:

Invalid boundary

9.4 错误 4

请注意区别,在参数中 files=​ 和 data= 提交数据的区别,其中:

files=​ -> multipart/form-data

data=​ -> application/x-www-form-urlencoded

Requests Body form-data
https://blog.mxsleon.com/blog/requests_form_data/blog/requests_form_data
Author mxsleon
Published at July 13, 2026