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

124

Я выполняю простую задачу по загрузке файла с помощью библиотеки запросов 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.

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

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

scichris
источник

Ответы:

211

Если 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" параметр.

Вы можете использовать кортеж для filesзначения сопоставления, содержащий от 2 до 4 элементов, если вам нужно больше контроля. Первым элементом является имя файла, за которым следует его содержимое, необязательное значение заголовка типа содержимого и необязательное отображение дополнительных заголовков:

files = {'upload_file': ('foobar.txt', open('file.txt','rb'), 'text/x-spam')}

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

Если вы имеете в виду, что все тело POST должно быть взято из файла (без указания других полей), тогда не используйте этот filesпараметр, просто отправьте файл напрямую как data. Затем вы можете также установить Content-Typeзаголовок, иначе ничего не будет установлено. См. Раздел Запросы Python - данные POST из файла .

Мартейн Питерс
источник
Привет! Как отправить несколько файлов с одним именем? Например, «привязанность».
Уильям Вино
4
@William: вы можете использовать последовательность 2-значение кортежей тоже, что позволяет повторно использовать имена полей: files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Каждый кортеж - это пара ключ и значение.
Martijn Pieters
2
Также вы можете использовать, files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}но если используется files = {}, то использовать headers = {'Content-Type': 'blah blah'} нельзя! -> @ martijn-pieters: так как Content-Type multipart / form-data должен включать граничное значение, используемое для разделения частей в теле сообщения. Отсутствие установки заголовка Content-Type гарантирует, что запросы установят для него правильное значение.
zaki
1
@MartijnPieters Не рискует ли утечка файла? Закрывает ли requestsэто?
Мэтт Мессерсмит
4
@MattMessersmith: нет, не закрыто. Если вы хотите закрыть файл, используйте with open(...) as fobj:и используйте fobjв filesсопоставлении.
Мартейн Питерс
36

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

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

r = requests.post(url, files=files)
r.text
laycat
источник
3
Библиотека запросов автоматически закрывает файл?
Деметрис
1
привет, прошло некоторое время с тех пор, как я использовал эту библиотеку. хороший вопрос. не могли бы вы помочь мне и остальным, набрав lsof | grep "filename" и поделитесь с нами своими результатами? спасибо :)
laycat
1
При использовании lsofкажется, что файл остается открытым, или, по крайней мере, так я интерпретирую следующие результаты. Раньше при запуске openв lsofтаблице не было записи о filename. Затем после openвыполнения появляется несколько записей с readдоступом. После выполнения requests.postзаписи все еще существуют, указывая, что файл не был закрыт.
Деметрис
23

Загрузка клиента

Если вы хотите загрузить один файл с requestsбиблиотекой Python , запросы 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 Form Data Parsing, как упоминалось в исправлении проблемы « загрузка больших файлов потребляет память », чтобы избежать неэффективного использования памяти при загрузке больших файлов (файл размером 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
gihanchanuka
источник
0

В 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)
Харшит Триведи
источник
какое значение dataпеременной?
am.rez
это может быть что угодно, например имя пользователя, я только что показал, как загружать файлы в REST apis
Harshit Tvedi