Używanie MultipartPostHandler do publikowania danych formularzy w Pythonie

Problem: podczas wysyłania danych z urllib2 Pythona, wszystkie dane są kodowane URL i wysyłane jako Content-Type: application / x-www-form-urlencoded. Podczas przesyłania plików typ zawartości powinien być ustawiony na multipart / form-data, a zawartość powinna być zakodowana MIME. Omówienie tego problemu znajduje się tutaj: http://code.activestate.com/recipes/146306/

Aby obejść to ograniczenie, niektórzy programiści sharp stworzyli bibliotekę o nazwie MultipartPostHandler, która tworzy openerdirector może używać z urllib2 do automatycznego publikowania z multipart / form-data. Kopia tej biblioteki jest tutaj: http://peerit.blogspot.com/2007/07/multipartposthandler-doesnt-work-for.html

Jestem nowy w Pythonie i nie jestem w stanie uruchomić tej biblioteki. Napisałem zasadniczo następujący kod. Kiedy rejestruję je w lokalnym proxy HTTP, widzę, że dane są nadal zakodowane URL, a nie wieloczęściowy kod MIME. Proszę, pomóż mi zrozumieć, co robię źle lub lepszy sposób na załatw to. Dzięki : -)

FROM_ADDR = '[email protected]'

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: dzięki za odpowiedź. Jestem świadomy rozwiązania ActiveState httplib do tego (połączyłem się z nim powyżej). Wolałbym raczej usunąć problem i użyć minimalnej ilości kodu, aby kontynuować korzystanie z urllib2, jak byłem. Jakiś pomysł, dlaczego Otwieracz nie jest instalowany i używany?

Author: Dan, 2009-03-25

6 answers

Wydaje się, że najprostszym i najbardziej kompatybilnym sposobem obejścia tego problemu jest użycie modułu 'poster'.

# 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()

To działało idealnie i nie musiałem sypać z httplib. Moduł jest dostępny tutaj: http://atlee.ca/software/poster/index.html

 57
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-27 01:31:25

Znaleziono ten przepis do post multipart używając httplib bezpośrednio (bez zewnętrznych bibliotek zaangażowanych)

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'
 35
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-02 21:51:03

Wystarczy użyć python-prośby, ustawi odpowiednie nagłówki i prześle za ciebie:

import requests 
files = {"form_input_field_name": open("filename", "rb")}
requests.post("http://httpbin.org/post", files=files)
 31
Author: Pawel Miech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-13 14:28:41

Napotkałem ten sam problem i musiałem zrobić wieloczęściowy formularz post bez korzystania z zewnętrznych bibliotek. Napisałem cały blog o problemach, na które wpadłem .

Skończyło się na użyciu zmodyfikowanej wersji http://code.activestate.com/recipes/146306/. kod w tym adresie URL w rzeczywistości dodaje zawartość pliku jako ciąg znaków, co może powodować problemy z plikami binarnymi. Oto mój Kodeks pracy.

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
 1
Author: Jason Kulatunga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-31 23:09:58

Ja co za zbieg okoliczności, 2 lata, 6 miesięcy temu tworzę projekt

Https://pypi.python.org/pypi/MultipartPostHandler2 , który naprawia MultipartPostHandler dla Systemów utf-8. Zrobiłem też kilka drobnych ulepszeń, zapraszam do testowania:)

 0
Author: Sérgio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 01:18:06

Aby odpowiedzieć na pytanie OP, dlaczego oryginalny kod nie działał, przekazywany przez niego handler nie był instancją klasy. Linia

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

Powinno być

opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler())
 0
Author: Daryl Tester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-27 13:27:48