def handle_uploaded_file(f: File, path: Optional[List[str]] = None, name: Optional[str] = None, max_length: Optional[int] = None) -> str: """Handle an uploaded file by a form and return the complete file's path path has to be iterable """ def get_name(file: File) -> str: if hasattr(file, 'name'): name = file.name elif hasattr(file, '_name'): name = file._name else: name = 'file_{:08x}'.format(randint(0, MAXINT)) if name.rpartition('.')[2] not in settings.ALLOWED_EXTENSIONS: name = f'{name}.txt' return name dir_path_length = 1 # For the final '/' if not hasattr(path, '__iter__'): # TODO: path is None (or add support for only one string) relative_dir_path = 'upload' dir_path = join(settings.MEDIA_ROOT, relative_dir_path) dir_path_length += len(relative_dir_path) else: relative_dir_path = join(*path) dir_path = join(settings.MEDIA_ROOT, *path) # The storage uses '/' even on Windows. dir_path_length += len('/'.join(relative_dir_path)) if not name: name = get_name(f) if max_length: max_length -= dir_path_length if max_length <= 0: raise ValueError('The max length is too small.') final_path = FileCreator(dir_path=dir_path, name=name, max_length=max_length).create() with open(final_path, 'wb', 0o755) as destination: for chunk in f.chunks(): destination.write(chunk) return join(relative_dir_path, basename(final_path))