Python 请求:在单个请求中发布 JSON 和文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19439961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:44:44  来源:igfitidea点击:

Python Requests: Post JSON and file in single request

pythonjsonurllib2

提问by oznu

I need to do a API call to upload a file along with a JSON string with details about the file.

我需要调用 API 来上传文件以及包含文件详细信息的 JSON 字符串。

I am trying to use the python requests lib to do this:

我正在尝试使用 python 请求库来执行此操作:

import requests

info = {
    'var1' : 'this',
    'var2'  : 'that',
}

data = json.dumps({
    'token' : auth_token,
    'info'  : info,
})

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

files = {'document': open('file_name.pdf', 'rb')}

r = requests.post(url, files=files, data=data, headers=headers)

This throws the following error:

这会引发以下错误:

    raise ValueError("Data must not be a string.")
 ValueError: Data must not be a string

If I remove the 'files' from the request, it works.
If I remove the 'data' from the request, it works.
If I do not encode data as JSON it works.

如果我从请求中删除“文件”,它就可以工作。
如果我从请求中删除“数据”,它就可以工作。
如果我不将数据编码为 JSON,它就可以工作。

For this reason I think the error is to do with sending JSON data and files in the same request.

出于这个原因,我认为错误与在同一请求中发送 JSON 数据和文件有关。

Any ideas on how to get this working?

关于如何让这个工作的任何想法?

采纳答案by proteneer

Don't encode using json.

不要使用 json 编码。

import requests

info = {
    'var1' : 'this',
    'var2'  : 'that',
}

data = {
    'token' : auth_token,
    'info'  : info,
}

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

files = {'document': open('file_name.pdf', 'rb')}

r = requests.post(url, files=files, data=data, headers=headers)

Note that this may not necessarily be what you want, as it will become another form-data section.

请注意,这可能不一定是您想要的,因为它将成为另一个表单数据部分。

回答by AChampion

I'm don't think you can send both data and files in a multipart encoded file, so you need to make your data a "file" too:

我不认为您可以在多部分编码的文件中同时发送数据和文件,因此您也需要将数据设为“文件”:

files = {
    'data' : data,
    'document': open('file_name.pdf', 'rb')
}

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

回答by ralf htp

See this thread How to send JSON as part of multipart POST-request

请参阅此线程如何将 JSON 作为多部分 POST 请求的一部分发送

Do not set the Content-type header yourself, leave that to pyrequests to generate

不要自己设置 Content-type 标头,将其留给 pyrequests 生成

def send_request():
payload = {"param_1": "value_1", "param_2": "value_2"}
files = {
     'json': (None, json.dumps(payload), 'application/json'),
     'file': (os.path.basename(file), open(file, 'rb'), 'application/octet-stream')
}

r = requests.post(url, files=files)
print(r.content)

回答by tc_qa

What is more:

更:

files = {
    'document': open('file_name.pdf', 'rb')
}

That will only work if your file is at the same directory where your script is.

只有当您的文件位于脚本所在的同一目录中时,这才有效。

If you want to append file from different directory you should do:

如果你想从不同的目录追加文件,你应该这样做:

files = {
    'document': open(os.path.join(dir_path, 'file_name.pdf'), 'rb')
}

Where dir_pathis a directory with your 'file_name.pdf'file.

其中dir_path是包含“file_name.pdf”文件的目录。

But what if you'd like to send multiple PDFs ?

但是如果您想发送多个 PDF 怎么办?

You can simply make a custom function to return a list of files you need (in your case that can be only those with .pdfextension). That also includes files in subdirectories (search for files recursively):

您可以简单地创建一个自定义函数来返回您需要的文件列表(在您的情况下,只能是那些扩展名为.pdf 的文件)。这也包括子目录中的文件(递归搜索文件):

def prepare_pdfs():
    return sorted([os.path.join(root, filename) for root, dirnames, filenames in os.walk(dir_path) for filename in filenames if filename.endswith('.pdf')])

Then you can call it:

然后你可以调用它:

my_data = prepare_pdfs()

And with simple loop:

并使用简单的循环:

for file in my_data:

    pdf = open(file, 'rb')

    files = {
        'document': pdf
    }

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

回答by wannik

For sending Facebook Messenger API, I changed all the payload dictionary values to be strings. Then, I can pass the payload as dataparameter.

为了发送 Facebook Messenger API,我将所有负载字典值更改为字符串。然后,我可以将有效负载作为data参数传递。

import requests

ACCESS_TOKEN = ''

url = 'https://graph.facebook.com/v2.6/me/messages'
payload = {
        'access_token' : ACCESS_TOKEN,
        'messaging_type' : "UPDATE",
        'recipient' : '{"id":"1111111111111"}',
        'message' : '{"attachment":{"type":"image", "payload":{"is_reusable":true}}}',
}
files = {'filedata': (file, open(file, 'rb'), 'image/png')}
r = requests.post(url, files=files, data=payload)

回答by SuperNova

I have been using requests==2.22.0

我一直在使用 requests==2.22.0

For me , the below code worked.

对我来说,下面的代码有效。

import requests


data = {
    'var1': 'this',
    'var2': 'that'
}

r = requests.post("http://api.example.com/v1/api/some/",
    files={'document': open('doocument.pdf', 'rb')},
    data=data,
    headers={"Authorization": "Token jfhgfgsdadhfghfgvgjhN"}. #since I had to authenticate for the same
)

print (r.json())