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


Ответы:


219

От: 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": ""
}

2
Я пытаюсь сделать то же самое, и он работает нормально, если размер файла меньше ~ 1,5 МБ. иначе выдает ошибку .. пожалуйста, посмотрите здесь .
Niks Jain

1
то, что я пытаюсь сделать, - это войти на какой-то сайт, используя запрос, который я успешно выполнил, но теперь я хочу загрузить видео после входа в систему, и в форме есть другие поля, которые необходимо заполнить перед отправкой. Итак, как мне передать такие значения, как описание видео, название видео и т. Д.
TaraGurung

15
Вы, вероятно, захотите сделать это with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f})вместо этого, чтобы он снова закрыл файл после открытия.
Hjulle

3
А? С каких это пор при отправке запросов все так просто?
palsch

1
Этот ответ должен быть обновлен, чтобы включить предложение Hjulle об использовании диспетчера контекста для обеспечения закрытия файла.
bmoran

28

Да. Вы должны использовать 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!')

1
На python 2.6.6 я получал ошибку при синтаксическом разборе границ Multipart при использовании этого кода в Windows. Мне пришлось перейти с string.letters на string.ascii_letters, как обсуждалось на stackoverflow.com/questions/2823316/…, чтобы это сработало. Требование к границе обсуждается здесь: stackoverflow.com/questions/147451/…
amit

вызов run_upload ({'server': '', 'thread': ''}, paths = ['/ path / to / file.txt']) вызывает ошибку в этой строке: upload_file (путь), поскольку для "файла загрузки" требуется 3 параметра, поэтому я заменяю его этой строкой upload_file (path, 1, 1)
tabdulradi

4

Единственное, что мешает вам использовать 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

@robert Я тестирую ваш код на Python2.7, но он не работает. urlopen (Request (theUrl, theFile, ...)) просто кодирует содержимое файла, как если бы это была обычная запись, но не может указать правильное поле формы. Я даже пробую вариант urlopen (theUrl, urlencode ({'serveride_field_name': EnhancedFile ('my_file.txt')})), он загружает файл, но (конечно!) С некорректным содержимым как <open file 'my_file.txt', режим 'r' в 0x00D6B718>. Я что-то пропустил?
RayLuo 04

Спасибо за ответ . Используя приведенный выше код, я перенес 2,2 ГБ необработанного файла изображения с помощью запроса PUT на веб-сервер.
Акшай Патил


2

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


2

Я пытаюсь протестировать 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)

1
этот код обеспечивает утечку памяти - вы забыли close()файл.
Chiefir 03

0

Вы также можете посмотреть httplib2 с примерами . Я считаю, что использование httplib2 более лаконично, чем использование встроенных модулей HTTP.


2
Нет примеров, показывающих, как поступать с загрузкой файлов.
dland

Ссылка устарела + нет встроенного примера.
jlr

3
С тех пор он переехал на github.com/httplib2/httplib2 . С другой стороны, в настоящее время я бы, вероятно, рекомендовал requestsвместо этого.
pdc

0
def visit_v2(device_code, camera_code):
    image1 = MultipartParam.from_file("files", "/home/yuzx/1.txt")
    image2 = MultipartParam.from_file("files", "/home/yuzx/2.txt")
    datagen, headers = multipart_encode([('device_code', device_code), ('position', 3), ('person_data', person_data), image1, image2])
    print "".join(datagen)
    if server_port == 80:
        port_str = ""
    else:
        port_str = ":%s" % (server_port,)
    url_str = "http://" + server_ip + port_str + "/adopen/device/visit_v2"
    headers['nothing'] = 'nothing'
    request = urllib2.Request(url_str, datagen, headers)
    try:
        response = urllib2.urlopen(request)
        resp = response.read()
        print "http_status =", response.code
        result = json.loads(resp)
        print resp
        return result
    except urllib2.HTTPError, e:
        print "http_status =", e.code
        print e.read()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.