python2/alibabacloud_tea_fileform/file_form.py (126 lines of code) (raw):

import os import sys from _io import BytesIO from alibabacloud_tea_fileform.models import FileField from Tea.stream import BaseStream from Tea.converter import TeaConverter as TC FMT = b'[%s]' class FileFormInputStream(BaseStream): MAX_SIZE = 2147483647 def __init__(self, form, boundary, size=8192): super(FileFormInputStream, self).__init__(size) self.form = form self.boundary = boundary self.file_size_left = 0 self.forms = {} self.files = {} self.files_keys = [] self._to_map() self.form_str = '' self._build_str_forms() self.str_length = len(self.form_str) def _to_map(self): for k in sorted(self.form): if isinstance(self.form[k], FileField): self.files[k] = self.form[k] self.files_keys.append(k) else: self.forms[k] = self.form[k] def _build_str_forms(self): form_str = '' str_fmt = '--%s\r\nContent-Disposition: form-data; name="%s"\r\n\r\n%s\r\n' forms_list = sorted(list(self.forms)) for key in forms_list: value = self.forms[key] form_str += str_fmt % (self.boundary, key, value) self.form_str = TC.to_bytes(form_str) def _get_stream_length(self): file_length = 0 for k, ff in self.files.items(): field_length = len(TC.to_bytes(ff.filename)) + len(ff.content_type) +\ len(TC.to_bytes(k)) + len(self.boundary) + 78 if isinstance(ff.content, BytesIO): file_length += len(ff.content.getvalue()) + field_length else: file_length += os.path.getsize(ff.content.name) + field_length stream_length = self.str_length + file_length + len(self.boundary) + 6 return stream_length def __len__(self): return self._get_stream_length() def __iter__(self): return self def __next__(self): return self.read(self.size, loop=True) def next(self): return self.read(self.size, loop=True) def file_str(self, size): # handle file object form_str = b'' start_fmt = '--%s\r\nContent-Disposition: form-data; name="%s";' content_fmt = b' filename="[%s]"\r\nContent-Type: [%s]\r\n\r\n[%s]' if self.file_size_left: for key in self.files_keys[:]: if size <= 0: break file_field = self.files[key] file_content = TC.to_bytes(file_field.content.read(size)) if self.file_size_left <= size: form_str += b'[%s]\r\n'.replace(FMT, file_content, 1) self.file_size_left = 0 size -= len(file_content) self.files_keys.remove(key) else: form_str += file_content self.file_size_left -= size size -= len(file_content) else: for key in self.files_keys[:]: if size <= 0: break file_field = self.files[key] if isinstance(file_field.content, BytesIO): file_size = len(file_field.content.getvalue()) else: file_size = os.path.getsize(file_field.content.name) self.file_size_left = file_size file_content = TC.to_bytes(file_field.content.read(size)) # build form_str start = start_fmt % (self.boundary, key) content = content_fmt.replace(FMT, TC.to_bytes(file_field.filename), 1)\ .replace(FMT, TC.to_bytes(file_field.content_type), 1)\ .replace(FMT, file_content, 1) if self.file_size_left < size: form_str += b'[%s][%s]\r\n'.replace(FMT, TC.to_bytes(start), 1).replace(FMT, content, 1) self.file_size_left = 0 size -= len(file_content) self.files_keys.remove(key) else: form_str += b'[%s][%s]'.replace(FMT, TC.to_bytes(start), 1).replace(FMT, content, 1) self.file_size_left -= size size -= len(file_content) return form_str def read(self, size=None, loop=False): if not self.files_keys and not self.form_str: self.refresh() if loop: raise StopIteration else: return b'' if size is None: size = self.MAX_SIZE if self.form_str: form_str = self.form_str[:size] self.form_str = self.form_str[size:] if len(form_str) < size: form_str += self.file_str(size) else: form_str = self.file_str(size) if not self.form_str and not self.files_keys: form_str += b'--[%s]--\r\n'.replace(FMT, TC.to_bytes(self.boundary), 1) return form_str def refresh_cursor(self): for ff in self.files.values(): ff.content.seek(0, 0) def refresh(self): self.file_size_left = 0 self._to_map() self._build_str_forms() self.refresh_cursor()