Запросы 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 в один и тот же запрос.
Любые идеи о том, как это сделать?
Ответы
Ответ 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, ...)