Подтвердить что ты не робот

Запросы Python: сообщение JSON и файл в одном запросе

Мне нужно сделать вызов API для загрузки файла вместе со строкой JSON с подробностями о файле.

Я пытаюсь использовать запросы python lib для этого:

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 в один и тот же запрос.

Любые идеи о том, как это сделать?

4b9b3361

Ответ 1

Не кодируйте с помощью 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)

Обратите внимание, что это необязательно может быть тем, что вы хотите, так как оно станет другим сектором данных формы.

Ответ 2

Смотрите эту тему Как отправить 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)

Ответ 3

Я не думаю, что вы можете отправлять как данные, так и файлы в многострочном закодированном файле, поэтому вам также нужно сделать ваши данные "файлом":

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

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

Ответ 4

Что еще:

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

Это будет работать, только если ваш файл находится в том же каталоге, где находится ваш script.

Если вы хотите добавить файл из другого каталога, вы должны сделать:

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, ...)