Отправить файл с помощью POST из скрипта Python

Есть ли способ отправить файл с помощью POST из скрипта Python?


person readonly    schedule 16.09.2008    source источник


Ответы (8)


От: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file

Запросы упрощают загрузку файлов с кодировкой Multipart:

with open('report.xls', 'rb') as f:
    r = requests.post('http://httpbin.org/post', files={'report.xls': f})

Вот и все. Я не шучу - это одна строчка кода. Файл отправлен. Давайте проверим:

>>> r.text
{
  "origin": "179.13.100.4",
  "files": {
    "report.xls": "<censored...binary...data>"
  },
  "form": {},
  "url": "http://httpbin.org/post",
  "args": {},
  "headers": {
    "Content-Length": "3196",
    "Accept-Encoding": "identity, deflate, compress, gzip",
    "Accept": "*/*",
    "User-Agent": "python-requests/0.8.0",
    "Host": "httpbin.org:80",
    "Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
  },
  "data": ""
}
person Piotr Dobrogost    schedule 19.04.2012
comment
Я пытаюсь сделать то же самое, и он работает нормально, если размер файла меньше ~ 1,5 МБ. иначе выдает ошибку ... пожалуйста, посмотрите здесь. - person Niks Jain; 28.11.2013
comment
то, что я пытаюсь сделать, - это войти на какой-то сайт, используя запрос, который я успешно выполнил, но теперь я хочу загрузить видео после входа в систему, и в форме есть другие поля, которые необходимо заполнить перед отправкой. Итак, как мне передать эти значения, такие как описание видео, название видео и т. Д. - person TaraGurung; 31.05.2015
comment
Вы, вероятно, захотите вместо этого сделать with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f}), чтобы он снова закрыл файл после открытия. - person Hjulle; 08.08.2015
comment
Этот ответ следует обновить, чтобы включить предложение Хджулле об использовании диспетчера контекста для обеспечения закрытия файла. - person bmoran; 27.01.2017
comment
у меня это не работает, там написано: «Метод 405 не разрешен». с open (file_path, 'rb') как f: response = requests.post (url = url, data = f, auth = HTTPBasicAuth (username = id, password = password)) - person Saurabh Jain; 17.07.2017
comment
У меня есть файл сырого формата .jpg, который хранится как ndarray переменная, как я могу загрузить его на сервер? - person Santhosh; 22.03.2018
comment
Можем ли мы отправлять PDF-файлы, используя это? Потому что это не только для текстовых файлов? - person Kavi Vaidya; 28.10.2018

да. Вы должны использовать модуль urllib2 и кодировать с использованием типа содержимого multipart/form-data. Вот пример кода для начала - это немного больше, чем просто загрузка файлов, но вы должны иметь возможность прочитать его и посмотреть, как он работает:

user_agent = "image uploader"
default_message = "Image $current of $total"

import logging
import os
from os.path import abspath, isabs, isdir, isfile, join
import random
import string
import sys
import mimetypes
import urllib2
import httplib
import time
import re

def random_string (length):
    return ''.join (random.choice (string.letters) for ii in range (length + 1))

def encode_multipart_data (data, files):
    boundary = random_string (30)

    def get_content_type (filename):
        return mimetypes.guess_type (filename)[0] or 'application/octet-stream'

    def encode_field (field_name):
        return ('--' + boundary,
                'Content-Disposition: form-data; name="%s"' % field_name,
                '', str (data [field_name]))

    def encode_file (field_name):
        filename = files [field_name]
        return ('--' + boundary,
                'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),
                'Content-Type: %s' % get_content_type(filename),
                '', open (filename, 'rb').read ())

    lines = []
    for name in data:
        lines.extend (encode_field (name))
    for name in files:
        lines.extend (encode_file (name))
    lines.extend (('--%s--' % boundary, ''))
    body = '\r\n'.join (lines)

    headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
               'content-length': str (len (body))}

    return body, headers

def send_post (url, data, files):
    req = urllib2.Request (url)
    connection = httplib.HTTPConnection (req.get_host ())
    connection.request ('POST', req.get_selector (),
                        *encode_multipart_data (data, files))
    response = connection.getresponse ()
    logging.debug ('response = %s', response.read ())
    logging.debug ('Code: %s %s', response.status, response.reason)

def make_upload_file (server, thread, delay = 15, message = None,
                      username = None, email = None, password = None):

    delay = max (int (delay or '0'), 15)

    def upload_file (path, current, total):
        assert isabs (path)
        assert isfile (path)

        logging.debug ('Uploading %r to %r', path, server)
        message_template = string.Template (message or default_message)

        data = {'MAX_FILE_SIZE': '3145728',
                'sub': '',
                'mode': 'regist',
                'com': message_template.safe_substitute (current = current, total = total),
                'resto': thread,
                'name': username or '',
                'email': email or '',
                'pwd': password or random_string (20),}
        files = {'upfile': path}

        send_post (server, data, files)

        logging.info ('Uploaded %r', path)
        rand_delay = random.randint (delay, delay + 5)
        logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay)
        time.sleep (rand_delay)

    return upload_file

def upload_directory (path, upload_file):
    assert isabs (path)
    assert isdir (path)

    matching_filenames = []
    file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE)

    for dirpath, dirnames, filenames in os.walk (path):
        for name in filenames:
            file_path = join (dirpath, name)
            logging.debug ('Testing file_path %r', file_path)
            if file_matcher.search (file_path):
                matching_filenames.append (file_path)
            else:
                logging.info ('Ignoring non-image file %r', path)

    total_count = len (matching_filenames)
    for index, file_path in enumerate (matching_filenames):
        upload_file (file_path, index + 1, total_count)

def run_upload (options, paths):
    upload_file = make_upload_file (**options)

    for arg in paths:
        path = abspath (arg)
        if isdir (path):
            upload_directory (path, upload_file)
        elif isfile (path):
            upload_file (path)
        else:
            logging.error ('No such path: %r' % path)

    logging.info ('Done!')
person John Millikin    schedule 16.09.2008
comment
На python 2.6.6 я получал ошибку при синтаксическом разборе границ Multipart при использовании этого кода в Windows. Мне пришлось перейти с string.letters на string.ascii_letters, как описано в stackoverflow.com/questions/2823316/, чтобы это работало. Требование к границе обсуждается здесь: stackoverflow.com/questions/147451/ - person amit; 19.01.2011
comment
вызов run_upload ({'server': '', 'thread': ''}, paths = ['/ path / to / file.txt']) вызывает ошибку в этой строке: upload_file (path), потому что для файла загрузки требуется 3 параметра поэтому я заменяю его этой строкой upload_file (path, 1, 1) - person tabdulradi; 20.10.2011

Единственное, что мешает вам использовать urlopen непосредственно для файлового объекта, - это то, что у встроенного файлового объекта отсутствует определение len. Простой способ - создать подкласс, который предоставляет urlopen с правильным файлом. Я также изменил заголовок Content-Type в файле ниже.

import os
import urllib2
class EnhancedFile(file):
    def __init__(self, *args, **keyws):
        file.__init__(self, *args, **keyws)

    def __len__(self):
        return int(os.fstat(self.fileno())[6])

theFile = EnhancedFile('a.xml', 'r')
theUrl = "http://example.com/abcde"
theHeaders= {'Content-Type': 'text/xml'}

theRequest = urllib2.Request(theUrl, theFile, theHeaders)

response = urllib2.urlopen(theRequest)

theFile.close()


for line in response:
    print line
person ilmarinen    schedule 01.11.2011
comment
@robert Я тестирую ваш код на Python2.7, но он не работает. urlopen (Request (theUrl, theFile, ...)) просто кодирует содержимое файла, как если бы это было обычное сообщение, но не может указать правильное поле формы. Я даже пробую вариант urlopen (theUrl, urlencode ({'serveride_field_name': EnhancedFile ('my_file.txt')})), он загружает файл, но (конечно!) С некорректным содержанием как ‹открытый файл 'my_file.txt', режим 'r' в 0x00D6B718 ›. Я что-то пропустил? - person RayLuo; 04.03.2013
comment
Спасибо за ответ . Используя приведенный выше код, я перенес 2,2 ГБ необработанного файла изображения с помощью запроса PUT на веб-сервер. - person Akshay Patil; 19.07.2013

Похоже, запросы python не обрабатывают очень большие файлы, состоящие из нескольких частей.

В документации рекомендуется изучить requests-toolbelt.

Вот соответствующая страница из их документации.

person rye    schedule 08.07.2015

Для этого очень хорошо подходит библиотека poster Криса Атли (особенно удобная функция poster.encode.multipart_encode()). В качестве бонуса он поддерживает потоковую передачу больших файлов без загрузки всего файла в память. См. Также проблему 3244 Python.

person gotgenes    schedule 08.02.2009

Я пытаюсь протестировать django rest api и его работу для меня:

def test_upload_file(self):
        filename = "/Users/Ranvijay/tests/test_price_matrix.csv"
        data = {'file': open(filename, 'rb')}
        client = APIClient()
        # client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
        response = client.post(reverse('price-matrix-csv'), data, format='multipart')

        print response
        self.assertEqual(response.status_code, status.HTTP_200_OK)
person Ranvijay    schedule 10.05.2016
comment
этот код обеспечивает утечку памяти - вы забыли close() файл. - person Chiefir; 03.01.2019

Вы также можете взглянуть на http://code.google.com/p/httplib2/ с примеры. Я считаю, что использование httplib2 более лаконично, чем использование встроенных модулей HTTP.

person pdc    schedule 16.09.2008
comment
Нет примеров, показывающих, как поступать с загрузкой файлов. - person dland; 15.09.2011
comment
Ссылка устарела + нет встроенного примера. - person jlr; 17.10.2016
comment
С тех пор он переместился на https://github.com/httplib2/httplib2. С другой стороны, в настоящее время я бы, вероятно, рекомендовал вместо этого requests. - person pdc; 01.03.2017

person    schedule
comment
Как правило, рекомендуется писать комментарии на английском языке. - person obotezat; 27.04.2021