Python запрашивает загрузку файла

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

import requests
url='http://nesssi.cacr.caltech.edu/cgi-bin/getmulticonedb_release2.cgi/post'
files={'files': open('file.txt','rb')}
values={'upload_file' : 'file.txt' , 'DB':'photcat' , 'OUT':'csv' , 'SHORT':'short'}
r=requests.post(url,files=files,data=values)

Я заполняю значение ключевого слова upload_file своим именем файла, потому что если я оставлю это поле пустым, оно говорит

Error - You must select a file to upload!

И теперь я получаю

File  file.txt  of size    bytes is  uploaded successfully!
Query service results:  There were 0 lines.

Который появляется только если файл пуст. Поэтому я застрял в том, как успешно отправить мой файл. Я знаю, что файл работает, потому что, если я захожу на этот веб-сайт и вручную заполняю форму, он возвращает хороший список совпавших объектов, что мне и нужно. Буду очень признателен за все подсказки.

Некоторые другие связанные темы (но не отвечающие на мою проблему):

10 ответов

Решение

Если upload_file это файл, используйте:

files = {'upload_file': open('file.txt','rb')}
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}

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

а также requests отправит тело POST из нескольких частей с upload_file поле, установленное на содержимое file.txt файл.

Имя файла будет включено в заголовок MIME для определенного поля:

>>> import requests
>>> open('file.txt', 'wb')  # create an empty demo file
<_io.BufferedWriter name='file.txt'>
>>> files = {'upload_file': open('file.txt', 'rb')}
>>> print(requests.Request('POST', 'http://example.com', files=files).prepare().body.decode('ascii'))
--c226ce13d09842658ffbd31e0563c6bd
Content-Disposition: form-data; name="upload_file"; filename="file.txt"


--c226ce13d09842658ffbd31e0563c6bd--

Обратите внимание filename="file.txt" параметр.

(2018) новая библиотека запросов Python упростила этот процесс, мы можем использовать переменную 'files', чтобы сигнализировать о том, что мы хотим загрузить многочастный кодированный файл

url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

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

Клиент Uplaod

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

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

Сторона сервера

Затем сохраните файл на server.py сторона такая, что сохранить поток в файл без загрузки в память. Ниже приведен пример использования загрузки файлов Flask.

@app.route("/upload", methods=['POST'])
def upload_file():
    from werkzeug.datastructures import FileStorage
    FileStorage(request.stream).save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return 'OK', 200

Или используйте синтаксический анализ данных формы werkzeug, как упоминалось в исправлении для проблемы " загрузки больших файлов, израсходовавшей память", чтобы избежать неэффективного использования памяти при загрузке больших файлов (файл st 22 ГиБ за ~60 секунд. Использование памяти постоянно около 13 МиБ.).

@app.route("/upload", methods=['POST'])
def upload_file():
    def custom_stream_factory(total_content_length, filename, content_type, content_length=None):
        import tempfile
        tmpfile = tempfile.NamedTemporaryFile('wb+', prefix='flaskapp', suffix='.nc')
        app.logger.info("start receiving file ... filename => " + str(tmpfile.name))
        return tmpfile

    import werkzeug, flask
    stream, form, files = werkzeug.formparser.parse_form_data(flask.request.environ, stream_factory=custom_stream_factory)
    for fil in files.values():
        app.logger.info(" ".join(["saved form name", fil.name, "submitted as", fil.filename, "to temporary file", fil.stream.name]))
        # Do whatever with stored file at `fil.stream.name`
    return 'OK', 200

Вы можете отправить любой файл через post api при вызове API, просто нужно упомянутьfiles={'any_key': fobj}

      import requests
import json
    
url = "https://request-url.com"
 
headers = {"Content-Type": "application/json; charset=utf-8"}
    
with open(filepath, 'rb') as fobj:
    response = requests.post(url, headers=headers, files={'file': fobj})
 
print("Status Code", response.status_code)
print("JSON Response ", response.json())

@martijn-pieters ответ правильный, однако я хотел добавить немного контекста в data=а также с другой стороны, на сервере Flask, в случае, когда вы пытаетесь загрузить файлы и JSON.

Со стороны запроса это работает, как описывает Мартейн:

      files = {'upload_file': open('file.txt','rb')}
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}

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

Однако на стороне Flask (получающий веб-сервер с другой стороны этого POST) мне пришлось использовать form

      @app.route("/sftp-upload", methods=["POST"])
def upload_file():
    if request.method == "POST":
        # the mimetype here isnt application/json
        # see here: https://stackoverflow.com/questions/20001229/how-to-get-posted-json-in-flask
        body = request.form
        print(body)  # <- immutable dict

body = request.get_json() ничего не вернет. body = request.get_data() вернет blob, содержащий множество вещей, таких как имя файла и т. д.

Плохая часть: на стороне клиента изменение data={} к json={}приводит к тому, что этот сервер не может читать пары KV! Как и в случае, это приведет к появлению {} тела выше:

      r = requests.post(url, files=files, json=values). # No!

Это плохо, потому что сервер не может контролировать формат запроса пользователем; а также json= станет привычкой запросов пользователей.

Загрузить:

      with open('file.txt', 'rb') as f:
    files = {'upload_file': f.read()}
    
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}

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

Скачать (Джанго):

      with open('file.txt', 'wb') as f:
    f.write(request.FILES['upload_file'].file.read())

Что касается ответов, данных до сих пор, всегда чего-то не хватало, что мешало ему работать на моей стороне. Итак, позвольте мне показать вам, что сработало для меня:

      import json
import os
import requests

API_ENDPOINT = "http://localhost:80"
access_token = "sdfJHKsdfjJKHKJsdfJKHJKysdfJKHsdfJKHs"  # TODO: get fresh Token here


def upload_engagement_file(filepath):
 
    url = API_ENDPOINT + "/api/files"  # add any URL parameters if needed
    hdr = {"Authorization": "Bearer %s" % access_token}
    with open(filepath, "rb") as fobj:
        file_obj = fobj.read()
        file_basename = os.path.basename(filepath)
        file_to_upload = {"file": (str(file_basename), file_obj)}
        finfo = {"fullPath": filepath}
        upload_response = requests.post(url, headers=hdr, files=file_to_upload, data=finfo)
        fobj.close()
    # print("Status Code ", upload_response.status_code)
    # print("JSON Response ", upload_response.json())
    return upload_response

Обратите внимание, чтоrequests.post(...)потребности

  • аurlпараметр, содержащий полный URL-адрес конечной точки API, которую вы вызываете, используяAPI_ENDPOINT, если у нас естьhttp://localhost:8000/api/filesконечная точка для POST файла
  • аheadersпараметр, содержащий как минимум авторизацию (токен на предъявителя)
  • аfilesпараметр, принимающий имя файла плюс все содержимое файла
  • аdataпараметр, принимающий только путь и имя файла

Требуется установка (консоль):

запросы на установку pip

В результате вызова функции вы получаете объект ответа, содержащий код состояния, а также полное сообщение об ошибке в формате JSON. Закомментированные операторы печати в концеupload_engagement_fileпоказывают вам, как вы можете получить к ним доступ.

Примечание. Полезную дополнительную информацию о библиотеке запросов можно найти здесь.

Некоторым может потребоваться загрузка через запрос на размещение, и это немного отличается от данных публикации. Важно понимать, как сервер ожидает данные, чтобы сформировать корректный запрос. Частый источник путаницы — отправка данных в многокомпонентной форме, когда они не принимаются. В этом примере используется базовая аутентификация и обновление изображения с помощью запроса на размещение.

      url = 'foobar.com/api/image-1'
basic = requests.auth.HTTPBasicAuth('someuser', 'password123')
# Setting the appropriate header is important and will vary based
# on what you upload
headers = {'Content-Type': 'image/png'} 
with open('image-1.png', 'rb') as img_1:
    r = requests.put(url, auth=basic, data=img_1, headers=headers)

Хотя библиотека запросов значительно упрощает работу с http-запросами, ее магия и удобство не позволяют понять, как создавать более тонкие запросы.

Определение ваших файлов может выглядеть следующим образом:

      file_to_upload = [
    ('image', ('example.jpg', open('example.jpg', 'rb'), 'image/jpg'))]

Он определяет имя файла для его сохранения, затем двоичные данные для отправки и тип контента — все это в кортеже. «изображение» — это поле формы, в которое передаются данные.

В Ubuntu вы можете применить этот способ,

чтобы сохранить файл в каком-либо месте (временно), а затем открыть и отправить его в API

      path = default_storage.save('static/tmp/' + f1.name, ContentFile(f1.read()))
      path12 = os.path.join(os.getcwd(), "static/tmp/" + f1.name)
      data={} #can be anything u want to pass along with File
      file1 = open(path12, 'rb')
      header = {"Content-Disposition": "attachment; filename=" + f1.name, "Authorization": "JWT " + token}
       res= requests.post(url,data,header)
Другие вопросы по тегам