Mit MultipartPostHandler form-data mit Python POST
-
22-08-2019 - |
Frage
Problem: Wenn Daten mit Python urllib2 Posting, alle Daten URL codiert und als Content-Type gesendet: application / x-www-form-urlencoded. Beim Hochladen von Dateien sollte der Content-Type stattdessen eingestellt werden, um multipart / form-data und der Inhalt sein MIME verschlüsselt. Eine Diskussion über dieses Problem ist hier: http://code.activestate.com/recipes/146306/
Um diese Einschränkung zu bekommen einige scharfe Programmierer erstellt eine Bibliothek namens MultipartPostHandler, die eine OpenerDirector schafft man mit urllib2 können meist automatisch mit multipart / form-data POST. Eine Kopie dieser Bibliothek ist hier: http://peerit.blogspot.com/2007/07/multipartposthandler -doesnt-work-for.html
Ich bin neu in Python und bin nicht in der Lage diese Bibliothek zu arbeiten. Ich schrieb im Wesentlichen den folgenden Code ein. Wenn ich es in einem lokalen HTTP-Proxy erfassen, kann ich sehen, dass die Daten noch URL-codiert ist und nicht mehrteiligen MIME verschlüsselt. Bitte helfen Sie mir herauszufinden, was ich tue, falsch oder ein besserer Weg, dies zu tun bekommen. Danke: -)
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: Vielen Dank für Ihre Antwort. Ich bin mir bewusst, der Activestate httplib Lösung dieses Problems (ich es oben verlinkten). Ich würde eher abstrakt das Problem weg und eine minimale Menge an Code verwenden, um weiterhin mit urllib2, wie ich war. Jede Idee, warum der Opener ist nicht installiert und verwendet?
Lösung
Es scheint, dass die einfachste und kompatible Art und Weise, um dieses Problem zu bekommen, ist das Plakat 'Modul zu verwenden.
# 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()
Das funktionierte perfekt und ich habe nicht mit httplib Dreck. Das Modul ist hier verfügbar: http://atlee.ca/software/poster/index.html
Andere Tipps
Dieses Rezept gefunden mehrt mit httplib
direkt schreiben (keine externen Bibliotheken beteiligt)
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'
Verwenden Sie einfach python-Anfragen , wird es richtige Header gesetzt und für Sie laden:
import requests
files = {"form_input_field_name": open("filename", "rb")}
requests.post("http://httpbin.org/post", files=files)
Ich lief in das gleiche Problem und ich musste ohne Verwendung von externen Bibliotheken eine mehrteilige Form Post tun. Ich schrieb eine ganze Blogpost über die Probleme Ich lief in .
ich am Ende mit einer modifizierten Version von http://code.activestate.com/recipes/146306 / . Der Code in dieser URL tatsächlich fügt nur den Inhalt der Datei als Zeichenfolge, die Probleme mit binären Dateien verursachen kann. Hier ist mein Arbeits Code.
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
ich, was für ein fallen zusammen, 2 Jahre, 6 Monate her schaffe ich das Projekt
https://pypi.python.org/pypi/MultipartPostHandler2 , die MultipartPostHandler fix für UTF-8-Systeme. Ich habe auch ein paar kleinere Verbesserungen gemacht, Sie sind herzlich eingeladen, es zu testen :)
die OPs Frage, warum hat der ursprüngliche Code zu beantworten, nicht funktioniert, der Handler übergeben war keine Instanz einer Klasse. Die Zeile
# MIME encode the POST payload
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
sollte lesen
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler())