def encode_multipart_formdata(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data mime format. :param fields: Dictionary of fields or list of (key, value) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ # copy requests imports in here: from io import BytesIO from requests.packages.urllib3.filepost import ( choose_boundary, six, writer, b, get_content_type ) body = BytesIO() if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body.write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): filename, data = value writer(body).write('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body.write(b('Content-Type: %s\r\n\r\n' % (get_content_type(filename)))) else: data = value writer(body).write('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body.write(b'Content-Type: text/plain\r\n\r\n') if isinstance(data, int): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): writer(body).write(data) else: body.write(data) body.write(b'\r\n') body.write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body.getvalue(), content_type
def encode_multipart_formdata(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data mime format. :param fields: Dictionary of fields or list of (key, value) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ # copy requests imports in here: from io import BytesIO from requests.packages.urllib3.filepost import (choose_boundary, six, writer, b, get_content_type) body = BytesIO() if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body.write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): filename, data = value writer(body).write('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body.write( b('Content-Type: %s\r\n\r\n' % (get_content_type(filename)))) else: data = value writer(body).write( 'Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body.write(b'Content-Type: text/plain\r\n\r\n') if isinstance(data, int): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): writer(body).write(data) else: body.write(data) body.write(b'\r\n') body.write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body.getvalue(), content_type
def _generator(callback=self._callback): """Overall generator. Note the callback caching.""" # set up counters used in the callback cur_bytes = 0 tot_bytes = self.size # start the content body with the form parameters if self._params: params_content = b(''.join( '%s%s' % (self._get_section_header(name), content) for name, content in self._params.items())) else: params_content = b'' yield params_content # follow up with the files if len(self._files) == 1: # simple case, only one file (included as any other form param) file_opts = self._files[0] yield b( self._get_section_header( name='file', filename=file_opts['name'], content_type=file_opts['type'], )) for chunk in stream_file(file_opts['path'], self._chunksize): cur_bytes += len(chunk) yield chunk if callback: callback(cur_bytes, tot_bytes, 0) else: # we need to group all files in a single multipart/mixed section file_boundary = choose_boundary() yield b( self._get_section_header( name='files', content_type='multipart/mixed; boundary=%s' % (file_boundary, ))) for index, file_opts in enumerate(self._files): yield b( self._get_section_header( filename=file_opts['name'], content_type=file_opts['type'], )) for chunk in stream_file(file_opts['path'], self._chunksize): cur_bytes += len(chunk) yield chunk if callback: callback(cur_bytes, tot_bytes, index) yield b('\r\n--%s--' % (file_boundary, )) yield b('\r\n--%s--\r\n' % (self._boundary, ))
def __init__(self, files, params=None, callback=None, chunksize=4096): self._boundary = choose_boundary() self._params = params self._callback = callback self._chunksize = chunksize # generate content type header self.headers = {"Content-Type": "multipart/form-data; boundary=%s" % (self._boundary,)} # prepare files self._files = [] for opts in files: if isinstance(opts, string_types): opts = {"path": opts} opts.setdefault("name", opts.get("name") or osp.basename(opts["path"])) opts.setdefault("type", opts.get("type") or guess_type(opts["name"])[0]) self._files.append(opts)
def _generator(callback=self._callback): """Overall generator. Note the callback caching.""" # set up counters used in the callback cur_bytes = 0 tot_bytes = self.size # start the content body with the form parameters if self._params: params_content = b(''.join( '%s%s' % (self._get_section_header(name), content) for name, content in self._params.items() )) else: params_content = b'' yield params_content # follow up with the files if len(self._files) == 1: # simple case, only one file (included as any other form param) file_opts = self._files[0] yield b(self._get_section_header( name='file', filename=file_opts['name'], content_type=file_opts['type'], )) for chunk in stream_file(file_opts['path'], self._chunksize): cur_bytes += len(chunk) yield chunk if callback: callback(cur_bytes, tot_bytes, 0) else: # we need to group all files in a single multipart/mixed section file_boundary = choose_boundary() yield b(self._get_section_header( name='files', content_type='multipart/mixed; boundary=%s' % (file_boundary, ) )) for index, file_opts in enumerate(self._files): yield b(self._get_section_header( filename=file_opts['name'], content_type=file_opts['type'], )) for chunk in stream_file(file_opts['path'], self._chunksize): cur_bytes += len(chunk) yield chunk if callback: callback(cur_bytes, tot_bytes, index) yield b('\r\n--%s--' % (file_boundary, )) yield b('\r\n--%s--\r\n' % (self._boundary, ))
def __init__(self, files, params=None, callback=None, chunksize=4096): self._boundary = choose_boundary() self._params = params self._callback = callback self._chunksize = chunksize # generate content type header self.headers = { 'Content-Type': 'multipart/form-data; boundary=%s' % (self._boundary, ) } # prepare files self._files = [] for opts in files: if isinstance(opts, string_types): opts = {'path': opts} opts.setdefault('name', opts.get('name') or osp.basename(opts['path'])) opts.setdefault('type', opts.get('type') or guess_type(opts['name'])[0]) self._files.append(opts)
def __init__(self, files, params=None, callback=None, chunksize=4096): self._boundary = choose_boundary() self._params = params self._files = [ {'path': o} if isinstance(o, string_types) else o for o in files ] self._callback = callback self._chunksize = chunksize # generate content type header self.headers = { 'Content-Type': 'multipart/form-data; boundary=%s' % (self._boundary, ) } # prepare files self._files = [] for opts in files: if isinstance(opts, string_types): opts = {'path': opts} opts.setdefault('name', opts.get('name') or osp.basename(opts['path'])) opts.setdefault('type', opts.get('type') or guess_type(opts['name'])[0]) self._files.append(opts)
def encode_multipart_formdata_stream(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data MIME format. :param fields: Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ body = [] def body_write(item): if isinstance(item, basestring): item = BytesIO(item) body.append(item) body_write_encode = lambda item: body.append(BytesIO(item.encode('utf-8'))) if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body_write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): if len(value) == 3: filename, data, content_type = value else: filename, data = value content_type = get_content_type(filename) body_write_encode('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body_write(b('Content-Type: %s\r\n\r\n' % (content_type,))) else: data = value body_write_encode('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body_write(b'\r\n') if isinstance(data, int): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): body_write_encode(data) else: body_write(data) body_write(b'\r\n') body_write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body, content_type
def encode_multipart_formdata_stream(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data MIME format. :param fields: Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ body = [] def body_write(item): if isinstance(item, bytes): item = BytesIO(item) elif isinstance(item, (str, unicode)): item = StringIO(item) body.append(item) body_write_encode = lambda item: body.append(BytesIO(item.encode('utf-8'))) if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body_write_encode('--%s\r\n' % (boundary)) if isinstance(value, tuple): if len(value) == 3: filename, data, content_type = value else: filename, data = value from mimetypes import guess_type content_type, _ = guess_type(filename) if content_type is None: content_type = 'application/octet-stream' body_write_encode('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body_write_encode('Content-Type: %s\r\n\r\n' % (content_type, )) else: data = value body_write_encode('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body_write(b'\r\n') if isinstance(data, (int, long)): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): body_write_encode(data) else: body_write(data) body_write(b'\r\n') body_write_encode('--%s--\r\n' % (boundary)) content_type = 'multipart/form-data; boundary=%s' % (boundary) return body, content_type
def generate_boundary(): return choose_boundary()