Send file using POST from a Python script Send file using POST from a Python script python python

Send file using POST from a Python script


From: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file

Requests makes it very simple to upload Multipart-encoded files:

with open('report.xls', 'rb') as f:    r = requests.post('http://httpbin.org/post', files={'report.xls': f})

That's it. I'm not joking - this is one line of code. The file was sent. Let's check:

>>> r.text{  "origin": "179.13.100.4",  "files": {    "report.xls": "<censored...binary...data>"  },  "form": {},  "url": "http://httpbin.org/post",  "args": {},  "headers": {    "Content-Length": "3196",    "Accept-Encoding": "identity, deflate, compress, gzip",    "Accept": "*/*",    "User-Agent": "python-requests/0.8.0",    "Host": "httpbin.org:80",    "Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"  },  "data": ""}


Yes. You'd use the urllib2 module, and encode using the multipart/form-data content type. Here is some sample code to get you started -- it's a bit more than just file uploading, but you should be able to read through it and see how it works:

user_agent = "image uploader"default_message = "Image $current of $total"import loggingimport osfrom os.path import abspath, isabs, isdir, isfile, joinimport randomimport stringimport sysimport mimetypesimport urllib2import httplibimport timeimport redef random_string (length):    return ''.join (random.choice (string.letters) for ii in range (length + 1))def encode_multipart_data (data, files):    boundary = random_string (30)    def get_content_type (filename):        return mimetypes.guess_type (filename)[0] or 'application/octet-stream'    def encode_field (field_name):        return ('--' + boundary,                'Content-Disposition: form-data; name="%s"' % field_name,                '', str (data [field_name]))    def encode_file (field_name):        filename = files [field_name]        return ('--' + boundary,                'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),                'Content-Type: %s' % get_content_type(filename),                '', open (filename, 'rb').read ())    lines = []    for name in data:        lines.extend (encode_field (name))    for name in files:        lines.extend (encode_file (name))    lines.extend (('--%s--' % boundary, ''))    body = '\r\n'.join (lines)    headers = {'content-type': 'multipart/form-data; boundary=' + boundary,               'content-length': str (len (body))}    return body, headersdef send_post (url, data, files):    req = urllib2.Request (url)    connection = httplib.HTTPConnection (req.get_host ())    connection.request ('POST', req.get_selector (),                        *encode_multipart_data (data, files))    response = connection.getresponse ()    logging.debug ('response = %s', response.read ())    logging.debug ('Code: %s %s', response.status, response.reason)def make_upload_file (server, thread, delay = 15, message = None,                      username = None, email = None, password = None):    delay = max (int (delay or '0'), 15)    def upload_file (path, current, total):        assert isabs (path)        assert isfile (path)        logging.debug ('Uploading %r to %r', path, server)        message_template = string.Template (message or default_message)        data = {'MAX_FILE_SIZE': '3145728',                'sub': '',                'mode': 'regist',                'com': message_template.safe_substitute (current = current, total = total),                'resto': thread,                'name': username or '',                'email': email or '',                'pwd': password or random_string (20),}        files = {'upfile': path}        send_post (server, data, files)        logging.info ('Uploaded %r', path)        rand_delay = random.randint (delay, delay + 5)        logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay)        time.sleep (rand_delay)    return upload_filedef upload_directory (path, upload_file):    assert isabs (path)    assert isdir (path)    matching_filenames = []    file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE)    for dirpath, dirnames, filenames in os.walk (path):        for name in filenames:            file_path = join (dirpath, name)            logging.debug ('Testing file_path %r', file_path)            if file_matcher.search (file_path):                matching_filenames.append (file_path)            else:                logging.info ('Ignoring non-image file %r', path)    total_count = len (matching_filenames)    for index, file_path in enumerate (matching_filenames):        upload_file (file_path, index + 1, total_count)def run_upload (options, paths):    upload_file = make_upload_file (**options)    for arg in paths:        path = abspath (arg)        if isdir (path):            upload_directory (path, upload_file)        elif isfile (path):            upload_file (path)        else:            logging.error ('No such path: %r' % path)    logging.info ('Done!')


The only thing that stops you from using urlopen directly on a file object is the fact that the builtin file object lacks a len definition. A simple way is to create a subclass, which provides urlopen with the correct file. I have also modified the Content-Type header in the file below.

import osimport urllib2class EnhancedFile(file):    def __init__(self, *args, **keyws):        file.__init__(self, *args, **keyws)    def __len__(self):        return int(os.fstat(self.fileno())[6])theFile = EnhancedFile('a.xml', 'r')theUrl = "http://example.com/abcde"theHeaders= {'Content-Type': 'text/xml'}theRequest = urllib2.Request(theUrl, theFile, theHeaders)response = urllib2.urlopen(theRequest)theFile.close()for line in response:    print line