استخدام MultipartPostHandler لنشر بيانات النموذج باستخدام Python
-
22-08-2019 - |
سؤال
مشكلة:عند نشر البيانات باستخدام urllib2 الخاص بـ Python، يتم ترميز جميع البيانات بعنوان URL وإرسالها كنوع محتوى:application/x-www-form-urlencoded.عند تحميل الملفات، يجب بدلاً من ذلك تعيين نوع المحتوى على بيانات متعددة الأجزاء/النموذج وأن تكون المحتويات مشفرة بـ MIME.مناقشة هذه المشكلة هنا:http://code.activestate.com/recipes/146306/
للتغلب على هذا القيد، أنشأ بعض المبرمجين المتميزين مكتبة تسمى MultipartPostHandler والتي تنشئ OpenerDirector يمكنك استخدامه مع urllib2 لإجراء عملية POST تلقائيًا في الغالب مع بيانات متعددة الأجزاء/النماذج.نسخة من هذه المكتبة هنا:http://peerit.blogspot.com/2007/07/multipartposthandler-doesnt-work-for.html
أنا جديد على لغة بايثون ولا أستطيع تشغيل هذه المكتبة.لقد كتبت بشكل أساسي الكود التالي.عندما ألتقطها في وكيل HTTP محلي، أستطيع أن أرى أن البيانات لا تزال مشفرة بعنوان URL وليست مشفرة بـ MIME متعددة الأجزاء.الرجاء مساعدتي في معرفة ما أفعله بشكل خاطئ أو طريقة أفضل لإنجاز ذلك.شكرًا :-)
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..."
تحرير 1:شكرا لأستجابتك.أنا على علم بحل ActiveState httplib لهذا (لقد قمت بربطه أعلاه).أفضل التخلص من المشكلة واستخدام الحد الأدنى من التعليمات البرمجية لمواصلة استخدام urllib2 كما كنت.هل لديك أي فكرة عن سبب عدم تثبيت أداة الفتح واستخدامها؟
المحلول
ويبدو أن أسهل وأكثر متوافقة طريقة للالتفاف على هذه المشكلة هو استخدام "المرسل حدة.
# 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()
وهذا عمل مثالية وأنا لم يكن لديك لالوحل مع httplib. وحدة متاحة هنا: http://atlee.ca/software/poster/index.html
نصائح أخرى
وجدت هذه الوصفة للنشر متعددة الأجزاء باستخدام httplib
مباشرة (أي المكتبات الخارجية المعنية)
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'
مجرد استخدام طلبات بايثون, ، سيقوم بتعيين الرؤوس المناسبة ويقوم بالتحميل نيابةً عنك:
import requests
files = {"form_input_field_name": open("filename", "rb")}
requests.post("http://httpbin.org/post", files=files)
لقد واجهت نفس المشكلة وكنت بحاجة إلى نشر نموذج متعدد الأجزاء دون استخدام مكتبات خارجية.لقد كتبت كاملة مدونة حول المشكلات التي واجهتها.
انتهى بي الأمر باستخدام نسخة معدلة من http://code.activestate.com/recipes/146306/.يقوم الكود الموجود في عنوان URL هذا في الواقع بإلحاق محتوى الملف كسلسلة، مما قد يسبب مشاكل في الملفات الثنائية.وهنا رمز العمل الخاص بي.
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
وأنا ما تتزامن، 2 سنة، 6 شهور I إنشاء المشروع
https://pypi.python.org/pypi/MultipartPostHandler2 ، أن إصلاح MultipartPostHandler ل أنظمة 8 حسابات الأمانة الأحادية. كما أنني قد فعلت بعض التحسينات الطفيفة، أنتم مدعوون لاختبار :)
لإجابة سؤال على OP لماذا رمز الأصلي لم تنجح، وكان معالج مرت في لا مثيل فئة. خط
# MIME encode the POST payload
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
ويجب قراءة
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler())