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.

Що з’являється, лише якщо файл порожній. Тому я застряг, як успішно надсилати свій файл. Я знаю, що файл працює, тому що якщо я заходжу на цей веб-сайт і вручну заповнюю форму, він повертає хороший список відповідних об'єктів, про що я і хочу. Я б дуже цінував усі підказки.

Деякі інші теми пов'язані (але не відповідають моїй проблемі):

Відповіді:


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надішле тіло форми з поштовою формою з 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')}але якщо використовується файли = {}, то заголовки = {'Content-Type': 'blah blah'} не повинні використовуватися! -> @ martijn-pieters: через те, що тип типу Content-Type / Multi-Data повинен містити граничне значення, яке використовується для розмежування частин у тілі повідомлення. Якщо не встановити заголовок Content-Type, то запити встановлюють його на правильне значення.
заки

1
@MartijnPieters Чи це не ризикує витік файлу? Чи requestsзакриває це?
Метт Мессерсміт

4
@MattMessersmith: ні, це не закрито. Якщо ви хочете закрити файл, використовуйте with open(...) as fobj:та використовуйте fobjу filesзіставленні.
Martijn Pieters

36

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

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

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

3
Чи автоматично бібліотека запитів закриває файл?
Дімітріс

1
привіт, минуло деякий час, оскільки я використав цю бібліотеку. приємне запитання. чи можете ви подати мені та іншим руку, набравши lsof | grep "ім'я файлу" та поділіться своїми результатами з нами? дякую :)
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збоку, щоб зберегти потік у файл, не завантажуючи його в пам'ять. Далі наводиться приклад використання файлів на колбу .

@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, як зазначено у виправленні для випуску " великих завантажень файлів, що з'їдають пам'ять ", щоб уникнути неефективного використання пам'яті для завантаження великих файлів (ст. 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

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 Trivedi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.