Question

Problème: Lorsque vous rédigez des données avec urllib2 Python, toutes les données sont codées URL et envoyées comme Content-Type: application / x-www-form-urlencoded. Lors du téléchargement de fichiers, le type de contenu devrait plutôt être multipart / form-data et le contenu MIME être codé. Une discussion de ce problème est ici: http://code.activestate.com/recipes/146306/

Pour contourner cette limitation des codeurs tranchantes ont créé une bibliothèque appelée MultipartPostHandler qui crée un OpenerDirector vous pouvez utiliser avec urllib2 POST pour la plupart automatiquement multipart / form-data. Une copie de cette bibliothèque est ici: http://peerit.blogspot.com/2007/07/multipartposthandler -doesnt-travail-for.html

Je suis nouveau à Python et je suis incapable d'obtenir cette bibliothèque pour travailler. Je rédigeai essentiellement le code suivant. Lorsque je capture dans un proxy HTTP local, je peux voir que les données sont toujours URL codées et non MIME en plusieurs parties codées. S'il vous plaît aidez-moi à comprendre ce que je fais mal ou une meilleure façon d'y parvenir. Merci: -)

FROM_ADDR = 'my@email.com'

try:
    data = open(file, 'rb').read()
except:
    print "Error: could not open file %s for reading" % file
    print "Check permissions on the file or folder it resides in"
    sys.exit(1)

# Build the POST request
url = "http://somedomain.com/?action=analyze"       
post_data = {}
post_data['analysisType'] = 'file'
post_data['executable'] = data
post_data['notification'] = 'email'
post_data['email'] = FROM_ADDR

# MIME encode the POST payload
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
urllib2.install_opener(opener)
request = urllib2.Request(url, post_data)
request.set_proxy('127.0.0.1:8080', 'http') # For testing with Burp Proxy

# Make the request and capture the response
try:
    response = urllib2.urlopen(request)
    print response.geturl()
except urllib2.URLError, e:
    print "File upload failed..."

EDIT1: Merci pour votre réponse. Je suis au courant de la solution de httplib ActiveState à ce (je lié à au-dessus). Je préfère abstraire le problème et utiliser une quantité minimale de code pour continuer à utiliser urllib2 comment je suis. Toute idée pourquoi l'ouvreur n'est pas installé et utilisé?

Était-ce utile?

La solution

Il semble que le plus simple et le plus compatible pour contourner ce problème est d'utiliser le module « affiche ».

# test_client.py
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2

# Register the streaming http handlers with urllib2
register_openers()

# Start the multipart/form-data encoding of the file "DSC0001.jpg"
# "image1" is the name of the parameter, which is normally set
# via the "name" parameter of the HTML <input> tag.

# headers contains the necessary Content-Type and Content-Length
# datagen is a generator object that yields the encoded parameters
datagen, headers = multipart_encode({"image1": open("DSC0001.jpg")})

# Create the Request object
request = urllib2.Request("http://localhost:5000/upload_image", datagen, headers)
# Actually do the request, and get the response
print urllib2.urlopen(request).read()

Cela a fonctionné parfaitement et je ne pas muck avec httplib. Le module est disponible ici: http://atlee.ca/software/poster/index.html

Autres conseils

trouvé cette recette pour poster multipart httplib directement (pas de bibliothèques externes impliqués)

import httplib
import mimetypes

def post_multipart(host, selector, fields, files):
    content_type, body = encode_multipart_formdata(fields, files)
    h = httplib.HTTP(host)
    h.putrequest('POST', selector)
    h.putheader('content-type', content_type)
    h.putheader('content-length', str(len(body)))
    h.endheaders()
    h.send(body)
    errcode, errmsg, headers = h.getreply()
    return h.file.read()

def encode_multipart_formdata(fields, files):
    LIMIT = '----------lImIt_of_THE_fIle_eW_$'
    CRLF = '\r\n'
    L = []
    for (key, value) in fields:
        L.append('--' + LIMIT)
        L.append('Content-Disposition: form-data; name="%s"' % key)
        L.append('')
        L.append(value)
    for (key, filename, value) in files:
        L.append('--' + LIMIT)
        L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
        L.append('Content-Type: %s' % get_content_type(filename))
        L.append('')
        L.append(value)
    L.append('--' + LIMIT + '--')
    L.append('')
    body = CRLF.join(L)
    content_type = 'multipart/form-data; boundary=%s' % LIMIT
    return content_type, body

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

Il suffit d'utiliser python-requests , il sera mis en-têtes appropriés et ne télécharger pour vous:

import requests 
files = {"form_input_field_name": open("filename", "rb")}
requests.post("http://httpbin.org/post", files=files)

Je suis tombé sur le même problème et je devais faire un poste de forme multipart sans utiliser des bibliothèques externes. J'ai écrit ensemble blogpost sur les questions que je courais dans .

Je fini par utiliser une version modifiée de http://code.activestate.com/recipes/146306 / . Le code de cette url en fait seulement le contenu ajoute du fichier sous forme de chaîne, ce qui peut causer des problèmes avec les fichiers binaires. Voici mon code de travail.

import mimetools
import mimetypes
import io
import http
import json


form = MultiPartForm()
form.add_field("form_field", "my awesome data")

# Add a fake file     
form.add_file(key, os.path.basename(filepath),
    fileHandle=codecs.open("/path/to/my/file.zip", "rb"))

# Build the request
url = "http://www.example.com/endpoint"
schema, netloc, url, params, query, fragments = urlparse.urlparse(url)

try:
    form_buffer =  form.get_binary().getvalue()
    http = httplib.HTTPConnection(netloc)
    http.connect()
    http.putrequest("POST", url)
    http.putheader('Content-type',form.get_content_type())
    http.putheader('Content-length', str(len(form_buffer)))
    http.endheaders()
    http.send(form_buffer)
except socket.error, e:
    raise SystemExit(1)

r = http.getresponse()
if r.status == 200:
    return json.loads(r.read())
else:
    print('Upload failed (%s): %s' % (r.status, r.reason))

class MultiPartForm(object):
    """Accumulate the data to be used when posting a form."""

    def __init__(self):
        self.form_fields = []
        self.files = []
        self.boundary = mimetools.choose_boundary()
        return

    def get_content_type(self):
        return 'multipart/form-data; boundary=%s' % self.boundary

    def add_field(self, name, value):
        """Add a simple field to the form data."""
        self.form_fields.append((name, value))
        return

    def add_file(self, fieldname, filename, fileHandle, mimetype=None):
        """Add a file to be uploaded."""
        body = fileHandle.read()
        if mimetype is None:
            mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        self.files.append((fieldname, filename, mimetype, body))
        return

    def get_binary(self):
        """Return a binary buffer containing the form data, including attached files."""
        part_boundary = '--' + self.boundary

        binary = io.BytesIO()
        needsCLRF = False
        # Add the form fields
        for name, value in self.form_fields:
            if needsCLRF:
                binary.write('\r\n')
            needsCLRF = True

            block = [part_boundary,
              'Content-Disposition: form-data; name="%s"' % name,
              '',
              value
            ]
            binary.write('\r\n'.join(block))

        # Add the files to upload
        for field_name, filename, content_type, body in self.files:
            if needsCLRF:
                binary.write('\r\n')
            needsCLRF = True

            block = [part_boundary,
              str('Content-Disposition: file; name="%s"; filename="%s"' % \
              (field_name, filename)),
              'Content-Type: %s' % content_type,
              ''
              ]
            binary.write('\r\n'.join(block))
            binary.write('\r\n')
            binary.write(body)


        # add closing boundary marker,
        binary.write('\r\n--' + self.boundary + '--\r\n')
        return binary

Quelle coïncidence, 2 ans, 6 mois je crée le projet

https://pypi.python.org/pypi/MultipartPostHandler2 , qui fixent pour MultipartPostHandler utf-8 systèmes. J'ai aussi fait quelques améliorations mineures, nous vous invitons à le tester :)

Pour répondre à la question de savoir pourquoi le code d'origine de l'OP ne fonctionnait pas, le gestionnaire passé en était pas une instance d'une classe. La ligne

# MIME encode the POST payload
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)

devrait lire

opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler())
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top