51 votes

Utilisation de MultipartPostHandler pour POST des données de formulaire avec Python

Problème: Lors de l'Affichage de données avec Python urllib2, toutes les données sont codées dans l'URL et envoyé en tant que Content-Type: application/x-www-form-urlencoded. Lors du téléchargement de fichiers, le Type de Contenu devrait plutôt être défini comme " multipart/form-data et le contenu au format MIME. Une discussion de ce problème est ici: http://code.activestate.com/recipes/146306/

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

Je suis nouveau sur le Python et n'arrive pas à obtenir cette bibliothèque pour travailler. J'ai écrit, pour l'essentiel, le code suivant. Quand je capture dans un local proxy HTTP, je peux voir que les données sont codées dans l'URL et pas de multi-partie au format MIME. Merci de m'aider à comprendre ce que je fais de mal ou une meilleure façon d'obtenir ce fait. 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 conscient de la ActiveState httplib solution (j'ai lié ci-dessus). Je préfère abstraction du problème et utiliser une quantité minimale de code de continuer à utiliser urllib2 la façon dont je l'ai été. Aucune idée de pourquoi l'ouvreur n'est pas installé et utilisé?

61voto

Dan Points 897

Il semble que le moyen le plus simple et le plus compatible de résoudre ce problème consiste à 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 n'ai pas eu à muck avec httplib. Le module est disponible ici: http://atlee.ca/software/poster/index.html

41voto

nosklo Points 75862

Trouvé cette recette pour publier en plusieurs parties en utilisant httplib directement (aucune bibliothèque externe impliquée)

 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'
 

32voto

Pawel Miech Points 2170

En 2013, utiliser les demandes :

 import requests #pip install requests if you don't have it already

url = 'http://localhost:5000/upload'
files = {'file':open('file1.txt')} #'file' => name of html input field
r = requests.post(url, files=files)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X