Python betiğinden POST kullanarak dosya gönderme


Yanıtlar:


214

Gönderen: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file

İstekler, Çok Parça kodlu dosyaları yüklemeyi çok kolaylaştırır:

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

Bu kadar. Şaka yapmıyorum - bu bir kod satırı. Dosya gönderildi. Hadi kontrol edelim:

>>> 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
Dosya boyutu ~ 1.5 MB daha az ise aynı şeyi ve onun iyi çalışıyor çalışıyorum. başka bir hata atıyor .. lütfen buraya bakın .
Niks Jain

1
ne yapmaya çalışıyorum başarıyla yaptım isteği kullanarak bazı siteye giriş olduğunu, ancak şimdi giriş yaptıktan sonra bir video yüklemek istiyorum ve form gönderilmeden önce doldurulması gereken farklı bir alana sahiptir. Peki, video açıklaması, video başlığı vb. Gibi değerleri nasıl
iletmeliyim

15
Bunun with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f})yerine muhtemelen yapmak istersiniz , bu nedenle dosyayı açtıktan sonra tekrar kapatır.
Ağustos'ta Hjulle

3
Ha? Ne zamandan beri istek gönderirken bu kadar basit?
palsch

1
Bu yanıt, Hjulle'ın dosyanın kapalı olduğundan emin olmak için bağlam yöneticisini kullanma önerisini içerecek şekilde güncellenmelidir.
bmoran

28

Evet. Kullanılacak ediyorum urllib2kullanarak modülü ve kodlamak multipart/form-dataiçerik türü. İşte başlamanız için bazı örnek kodlar - sadece dosya yüklemekten biraz daha fazlası, ancak bunu okuyabilmeniz ve nasıl çalıştığını görebilmeniz gerekir:

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'da, Windows'ta bu kodu kullanırken Çok Parçalı sınır ayrıştırmada bir hata alıyordum. Bunun çalışması için stackoverflow.com/questions/2823316/… adresinde tartışıldığı gibi string.letters 'den string.ascii_letters' e geçmek zorunda kaldım . Sınır gereksinimi burada tartışılmıştır: stackoverflow.com/questions/147451/…
amit

run_upload ({'server': '', 'thread': ''} çağrıldığında, paths = ['/ path / to / file.txt']) şu satırda hataya neden oluyor: "upload_file (path)" upload file "gerektiriyor 3 parametre bu yüzden bu satır ile değiştirir upload_file (yol, 1, 1)
Radian

4

Urlopen'i doğrudan bir dosya nesnesi üzerinde kullanmanızı engelleyen tek şey, yerleşik dosya nesnesinin len tanımından yoksun olmasıdır . Basit bir yol, urlopen'e doğru dosyayı sağlayan bir alt sınıf oluşturmaktır. Ayrıca aşağıdaki dosyada Content-Type üstbilgisini değiştirdim.

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 Kodunuzu Python2.7'de test ediyorum ama çalışmıyor. urlopen (İstek (theUrl, theFile, ...)) yalnızca dosya içeriğini normal bir gönderi gibi kodlar, ancak doğru form alanını belirleyemez. Varyant urlopen'i bile deniyorum (theUrl, urlencode ({'serverside_field_name': EnhancedFile ('my_file.txt')})), bir dosya yükler ama (tabii ki!) Yanlış içeriğe sahip <açık dosya 'my_file.txt' olarak, modu 'r' 0x00D6B718>. Bir şey mi kaçırdım?
RayLuo

Cevap için teşekkürler . Yukarıdaki kodu kullanarak web sunucusuna PUT isteği kullanarak 2.2 GB ham görüntü dosyasını aktarmıştım.
Akshay Patil

4

Görünüşe göre python istekleri çok büyük çok parçalı dosyaları işlemiyor.

Belgeler, incelemenizi önerir requests-toolbelt.

İşte onların dokümanlarındaki ilgili sayfa .


2

Chris Atlee'nin poster kütüphanesi bunun için gerçekten iyi çalışıyor (özellikle kolaylık işlevi poster.encode.multipart_encode()). Bonus olarak, bir dosyanın tamamını belleğe yüklemeden büyük dosyaların akışını destekler. Ayrıca bkz . Python sayı 3244 .


2

Ben django dinlenme api ve onun için çalışıyor test etmeye çalışıyorum:

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
bu kod bir bellek sızıntısı sağlar - close()bir dosyayı unuttun .
Chiefir

0

Ayrıca , örneklerle httplib2'ye bir göz atmak isteyebilirsiniz . Httplib2 kullanmanın yerleşik HTTP modüllerini kullanmaktan daha özlü olduğunu düşünüyorum.


2
Dosya yüklemeleriyle nasıl başa çıkılacağını gösteren hiçbir örnek yoktur.
dland

Bağlantı eskimiş + satır içi örnek yok.
jlr

3
O zamandan beri github.com/httplib2/httplib2'ye taşındı . Öte yandan, bugünlerde muhtemelen requestsbunun yerine tavsiye ederim .
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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.