53

我需要执行 API 调用来上传文件以及带有文件详细信息的 JSON 字符串。

我正在尝试使用 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)

这会引发以下错误:

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

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

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

关于如何使它工作的任何想法?

4

6 回答 6

28

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

不要自己设置 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)
于 2016-03-11T18:04:03.003 回答
27

不要使用 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)

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

于 2013-11-26T07:20:19.097 回答
4

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

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

r = requests.post(url, files=files, headers=headers)
于 2013-10-18T01:28:46.637 回答
2

我一直在使用requests==2.22.0

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

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())
于 2020-04-10T12:43:44.397 回答
1

为了发送 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)
于 2018-11-29T17:16:09.350 回答
-1

更:

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

仅当您的文件与脚本所在的目录相同时,这才有效。

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

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

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

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

您可以简单地创建一个自定义函数来返回您需要的文件列表(在您的情况下,只能是具有.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')])

然后你可以调用它:

my_data = prepare_pdfs()

并使用简单的循环:

for file in my_data:

    pdf = open(file, 'rb')

    files = {
        'document': pdf
    }

    r = requests.post(url, files=files, ...)
于 2016-11-28T12:47:15.377 回答