使用Python的requests模块在单个请求中上传多个文件

44

Python的requests模块提供了良好的文档,说明如何在一个请求中上传单个文件:

 files = {'file': open('report.xls', 'rb')}

我尝试通过使用以下代码来扩展该示例,以尝试上传多个文件:

 files = {'file': [open('report.xls', 'rb'), open('report2.xls, 'rb')]}

但是它导致了这个错误:

 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py",      line 1052, in splittype
 match = _typeprog.match(url)
 TypeError: expected string or buffer

使用此模块,是否可以在单个请求中上传文件列表,如何实现?


4
为什么没有被接受的答案?下面得票最高的答案不足够吗? - Wade Anderson
Ping/ Bumping。这些答案中有一个合适吗? - Wayne Werner
10个回答

61

要在单个请求中上传具有相同键值的文件列表,您可以创建一个元组列表,其中每个元组的第一项为键值,第二项为文件对象:

files = [('file', open('report.xls', 'rb')), ('file', open('report2.xls', 'rb'))]

假设我有一个文件名列表,是否可以使用列表推导来完成这个任务? - Maciek Semik
4
file_names = ["abc.txt", "pqr.txt"] files = [('file', open(f, 'rb')) for f in file_names] 这段代码的意思是,定义了一个包含两个文件名的列表file_names,然后利用列表推导式生成了一个包含元组的列表files,其中每个元组包含一个字符串'file'和通过使用open函数打开对应文件名f的二进制文件对象。 - Swapnil Suryawanshi

34

通过添加多个字典条目,可以上传具有不同键值的多个文件:

files = {'file1': open('report.xls', 'rb'), 'file2': open('otherthing.txt', 'rb')}
r = requests.post('http://httpbin.org/post', files=files)

有趣。我会尝试你的方法。我之前尝试使用列表是因为 Flask(Python Web 框架)说文件是一个 multidict,访问所有上传文件的方式是: request.files.getall('file') - user462455
2
我需要自己关闭文件描述符吗?还是像open('file', 'r') as f一样会自动关闭? - kaki gadol
@Lukasa,R语言中是否有类似的解决方案? - user5249203

25
文档中有明确的答案。
引用:
您可以在一个请求中发送多个文件。例如,假设您想将图像文件上传到带有多个文件字段“images”的HTML表单:
为此,只需将文件设置为元组列表(form_field_name,file_info):
url = 'http://httpbin.org/post'
multiple_files = [('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
                      ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)
r.text

# {
#  ...
#  'files': {'images': 'data:image/png;base64,iVBORw ....'}
#  'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
#  ...
# }

文件信息可以采用什么形式?我可以省略内容类型吗?文件信息还可以包括哪些内容?文档没有详细说明。 - A.R.
@AmauryRodriguez 我建议你查看源代码以获取所有细节。 - Wade Anderson

4

如果您有来自表单的文件并希望将其转发到其他URL或API,则可以参考以下示例,其中包含多个文件和其他要转发到其他URL的表单数据。

images = request.files.getlist('images')
files = []
for image in images:
    files.append(("images", (image.filename, image.read(), image.content_type)))
r = requests.post(url="http://example.com/post", data={"formdata1": "strvalue", "formdata2": "strvalue2"}, files=files)

非常感谢,这正是我所需要的! - omercotkd

3
你需要创建一个文件列表来上传多张图片:
file_list = [  
       ('Key_here', ('file_name1.jpg', open('file_path1.jpg', 'rb'), 'image/png')),
       ('key_here', ('file_name2.jpg', open('file_path2.jpg', 'rb'), 'image/png'))
   ]

r = requests.post(url, files=file_list)

如果您想在同一关键字上发送文件,您需要为每个元素保持相同的关键字,对于不同的关键字只需更改关键字。
来源:https://stackabuse.com/the-python-requests-module/

2
我有点困惑,但直接在请求中打开文件(虽然官方的请求指南中也这么写)并不是很“安全”。请尝试:

最初的回答:

import os
import requests
file_path = "/home/user_folder/somefile.txt"
files = {'somefile': open(file_path, 'rb')}
r = requests.post('http://httpbin.org/post', files=files)

Yes, all will be ok, but:

os.rename(file_path, file_path)

你将会得到:

最初的回答

PermissionError:The process cannot access the file because it is being used by another process

如果我说错了,请纠正我,但似乎文件仍然处于打开状态,我不知道任何关闭它的方法。

相反,我使用以下方法:

Original Answer翻译成"最初的回答"

import os
import requests
#let it be folder with files to upload
folder = "/home/user_folder/"
#dict for files
upload_list = []
for files in os.listdir(folder):
    with open("{folder}{name}".format(folder=folder, name=files), "rb") as data:
        upload_list.append(files, data.read())
r = request.post("https://httpbin.org/post", files=upload_list)
#trying to rename uploaded files now
for files in os.listdir(folder):
    os.rename("{folder}{name}".format(folder=folder, name=files), "{folder}{name}".format(folder=folder, name=files))

现在我们不会出现错误,所以我建议使用这种方式上传多个文件,否则可能会出现一些错误。 希望这个答案能帮助到某人并节省宝贵的时间。

原始答案:最初的回答


我认为你试图做的事情不会起作用,因为文件指针在被requests.post使用之前就已经关闭了。因此,我们只能选择保持打开状态。我认为我们可以将指针放入变量中,在使用后关闭它们,或者在with块内部进行并行打开和提交。指针将自动关闭。 - Mooncrater

1

使用这些方法,文件将自动关闭。

方法1

with open("file_1.txt", "rb") as f1, open("file_2.txt", "rb") as f2:
    files = [f1, f2]
    response = requests.post('URL', files=files)

但是当你打开多个文件时,这可能会变得相当冗长。
方法2:
files = [open("forms.py", "rb"), open("data.db", "rb")]
response = requests.post('URL', files=files)

# Closing all Files
for file in files: 
    file.close()

0
在其他答案中,files = [("file", (filename, fileobj)), ("file", (filename, fileobj))] 的方法对我来说不起作用,但是files={"file1": (filename, fileobj), "file2": (filename, fileobj)}可以。这只是一个备选方法,以防上述答案不起作用。

0
如果你有一个 Python 列表里面存放了多个文件,你可以使用列表推导式中的 eval() 来循环遍历 requests post 文件参数中的文件。
file_list = ['001.jpg', '002.jpg', '003.jpg']
files=[eval(f'("inline", open("{file}", "rb"))') for file in file_list ]

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

0
在我的情况下,上传文件夹中的所有图像只需在循环中添加带有索引的键,例如将键从“images”更改为“images[0]”。
 photosDir = 'allImages'
 def getFilesList(self):
        listOfDir = os.listdir(os.path.join(os.getcwd()+photosDir))
        setOfImg = []
        for key,row in enumerate(listOfDir):
            print(os.getcwd()+photosDir+str(row) , 'Image Path')
            setOfImg.append((
                'images['+str(key)+']',(row,open(os.path.join(os.getcwd()+photosDir+'/'+str(row)),'rb'),'image/jpg')
            ))
        print(setOfImg)
        return  setOfImg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接