Как мне использовать request.put() для загрузки файла с помощью Python?

Я пытаюсь использовать библиотеку запросов в Python для загрузки файла в репозиторий Fedora commons на локальном хосте. Я вполне уверен, что моя основная проблема заключается в непонимании open()/read() и того, что мне нужно сделать для отправки данных с помощью http-запроса.

def postBinary(fileName,dirPath,url):
    path = dirPath+'/'+fileName
    print('to ' + url + '\n' + path)
    openBin = {'file':(fileName,open(path,'rb').read())}
    headers = {'Slug': fileName} #not important
    r = requests.put(url, files=openBin,headers=headers, auth=HTTPBasicAuth('username', 'pass'))
    print(r.text)
    print("and the url used:")
    print(r.url)

This will successfully upload a file in the repository, but it will be slightly larger and corrupted after. For example an image that was 6.6kb became 6.75kb and was not openable anymore.

So how should I properly open and upload a file using put in python?

###Extra details:###

  • Когда я заменяю files=openBin на data=openBin, я получаю свой словарь и предполагаю, что данные представляют собой строку. Я не знаю, полезна ли эта информация или нет.
    file=FILE_NAME.extension&file=TYPE89a%24%02Q%03%E7%FF%00E%5B%19%FC%.... и размер файла увеличивается до нескольких мегабайт

  • Я использую специально put, потому что конечная точка Fedora RESTful HTTP API говорит использовать put.

Следующая команда работает:

curl -u username:password -H "Content-Type: text/plain" -X PUT -T /path/to/someFile.jpeg http://localhost:8080/fcrepo/rest/someFile.jpeg


person awscott    schedule 12.12.2017    source источник
comment
Посмотрите здесь (используя метод POST) или здесь (используя метод PUT).   -  person Mauro Baraldi    schedule 13.12.2017
comment
Информация о посте мне не помогла, за исключением того, что для сокета используется неправильный протокол. ссылка на метод PUT, которую вы отправляете, я уже пробовал, тоже не повезло :( создал файл, который поврежден   -  person awscott    schedule 13.12.2017


Ответы (1)


Обновлено

Использование requests.put() с параметром files отправляет закодированный запрос multipart/form-data, который сервер, похоже, не может обработать без повреждения данных, даже если объявлен правильный тип контента.

Команда curl просто выполняет PUT с необработанными данными, содержащимися в теле запроса. Аналогичный запрос можно создать, передав данные файла в параметре data. Укажите тип контента в шапке:

headers = {'Content-type': 'image/jpeg', 'Slug': fileName}
r = requests.put(url, data=open(path, 'rb'), headers=headers, auth=('username', 'pass'))

Вы можете изменить заголовок Content-type в соответствии с полезной нагрузкой по мере необходимости.


Попробуйте установить Content-type для файла.

Если вы уверены, что это текстовый файл, попробуйте text/plain, который вы использовали в своей команде curl, даже если вы загружаете файл jpeg? Однако для изображения в формате jpeg следует использовать image/jpeg.

В противном случае для произвольных двоичных данных вы можете использовать application/octet-stream:

openBin = {'file': (fileName, open(path,'rb'), 'image/jpeg' )}

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

person mhawke    schedule 12.12.2017
comment
эй, я пробовал текст типа контента, простой как заголовок, и различные другие. это можно вызвать для любого файла, поэтому я не указал. простой текст по-прежнему работает в завитке при загрузке, однако не работает в python с запросами, как и «image/jpeg» или «application/octet-stream». хотя я заметил, что в репозитории есть свойство hasMimeType: multipart/form-data;boundary=9f74e4d3067e4ce482bdc9e311b58d2d, это вообще помогает? - person awscott; 13.12.2017
comment
также спасибо за совет о чтении, я добавил это раньше во время отладки. плохо взять его - person awscott; 13.12.2017
comment
@awscott: кажется, что сервер неправильно обрабатывает запросы в формате multipart/form. Я обновил свой ответ, чтобы показать более простой метод, который создает запрос, аналогичный запросу рабочей команды curl. - person mhawke; 13.12.2017
comment
вы правы, это сработало! спасибо! Я сделал заголовок Content-Type для application/octet-stream, как вы предложили, и теперь он работает правильно. - person awscott; 13.12.2017
comment
r = request.put(url, data=open(path, 'rb'), headers=headers, auth=('username', 'pass')) - Относительно этого, если файл слишком большой, потоковая передача без чтения в память будет иметь больше смысла? Если да, то как это сделать? - person Simplecode; 10.12.2020
comment
@Simplecode: потоковые загрузки — это поведение по умолчанию, когда файлоподобный объект, в данном случае открытый файл, передается в аргументе data. - person mhawke; 31.12.2020