python - 如何将每个文件的自定义 header 添加到带有请求的分段上传?

标签 python http http-post python-requests multipart

python-requests 似乎支持自定义集合作为要上传的文件的列表/映射。

这反过来应该允许我添加自定义 header 字段,每个文件在一次分段上传中上传。

我怎样才能真正做到这一点?

最佳答案

python-requests appears to support custom collections as a list/mapping of files to upload.

This in turn should allow me to add custom header fields with each file being uploading within one multipart upload.

没有。列表/映射中的每个文件必须是文件对象(或内容字符串)、文件名和文件对象(或内容)的二元组或文件名、文件对象(或内容)和文件的三元组类型。其他任何事情都是非法的。

除非#1640被上游接受,在这种情况下,您所要做的就是使用文件名、文件对象(或内容)、文件类型和 header 字典的 4 元组。


此时正确的做法可能是使用不同的库。例如,如果您直接使用 urllib3 而不是通过 request 包装器,它会使您想要做的事情相当容易。您只需处理使用 urllib3 而不是 requests 的所有额外冗长。

与此同时,您可以针对requests 提交功能请求,将来可能会添加更简单的方法。


但有点令人沮丧的是,您需要的功能都在幕后,您就是无法使用它。

看起来干净地做事将是一场噩梦。但这都是非常简单的代码,我们可以很容易地破解它,所以让我们开始吧。

看看requests.PreparedRequest.prepare_body .它期望 files 中的每个文件都是文件名、内容或文件对象以及可选的内容类型的元组。它基本上只是读取任何文件对象以将它们转换为内容,然后将所有内容直接传递给 urllib3.filepost.encode_multipart_formdata。因此,除非我们想要替换此代码,否则我们将需要使用这些值之一来走私 header 。让我们通过传递 (filename, contents, (content_type, headers_dict)) 来做到这一点。因此,requests 本身没有变化。

那个呢urllib3.filepost.encode_multipart_formdata它叫什么?如您所见,如果您将文件的元组传递给它,它会调用一个名为 iter_field_objects 的函数。 ,最终调用 urllib3.fields.RequestField.from_tuples在每一个上。但是,如果您查看 from_tuples 替代构造函数,它表示它用于处理构造 RequestField 对象的“旧式”方式,而普通构造函数用于处理“新式”方式,这实际上确实允许您传递 header 。

所以,我们需要做的就是 monkeypatch iter_field_objects,将它的最后一行替换为使用新式方式的那一行,我们应该完成了。让我们试试:

import requests
import requests.packages.urllib3
from requests.packages.urllib3.fields import RequestField, guess_content_type
import six

old_iter_field_objects = requests.packages.urllib3.filepost.iter_field_objects
def iter_field_objects(fields):
    if isinstance(fields, dict):
        i = six.iteritems(fields)
    else:
        i = iter(fields)

    for field in i:
      if isinstance(field, RequestField):
        yield field
      else:
        name, value = field
        filename = value[0]
        data = value[1]
        content_type = value[2] if len(value)>2 else guess_content_type(filename)
        headers = None
        if isinstance(content_type, (tuple, list)):
            content_type, headers = content_type
        rf = RequestField(name, data, filename, headers)
        rf.make_multipart(content_type=content_type)
        yield rf
requests.packages.urllib3.filepost.iter_field_objects = iter_field_objects

现在:

>>> files = {'file': ('foo.txt', 'foo\ncontents\n'), 
...          'file2': ('bar.txt', 'bar contents', 'text/plain'),
...          'file3': ('baz.txt', 'baz contents', ('text/plain', {'header': 'value'}))}
>>. r = request.Request('POST', 'http://example.com', files=files)
>>> print r.prepare().body
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file3"; filename="baz.txt"
Content-Type: text/plain
header: value

baz contents
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file2"; filename="bar.txt"
Content-Type: text/plain

bar contents
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file"; filename="foo.txt"
Content-Type: text/plain

foo

多田!

请注意,您需要相对最新的 requests/urllib3 才能正常工作。我认为 requests 2.0.0 就足够了。

关于python - 如何将每个文件的自定义 header 添加到带有请求的分段上传?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19104924/

相关文章:

python - 如何将宽数据帧转换为长数据帧

python - 从 python 解释器内部创建管道

php - URL 不显示文件名或扩展名,怎么办? (PHP)

c# - 如何使用C#提交http表单

mysql - 如何在 golang 中编写一个通用方法以在任何 mysql 表中插入记录

python - Hotelling 在 python 中的 T^2 分数

python - 来自 worker 的 Heroku 数据库管理

java - Spring 支持 MIME 类型 "multipart/byteranges"

php - 使用 Postman 通过 HTTP POST 上传图片

javascript - 从 Spotify API 请求 reshresh_token 时如何修复 'error: invalid_grant Invalid authorization code'?