def download(self, file_url: str, goal_path: str = None, rename: str = None, file_exists: str = 'rename', show_msg: bool = False, **kwargs) -> tuple: """下载一个文件 \n :param file_url: 文件url :param goal_path: 存放路径 :param rename: 重命名文件,可不写扩展名 :param file_exists: 若存在同名文件,可选择 'rename', 'overwrite', 'skip' 方式处理 :param show_msg: 是否显示下载信息 :param kwargs: 连接参数 :return: 下载是否成功(bool)和状态信息(成功时信息为文件路径)的元组 """ # 生成的response不写入self._response,是临时的 goal_path = goal_path or OptionsManager().get_value( 'paths', 'global_tmp_path') if not goal_path: raise IOError('No path specified.') kwargs['stream'] = True if 'timeout' not in kwargs: kwargs['timeout'] = 20 r, info = self._make_response(file_url, mode='get', **kwargs) if not r: if show_msg: print(info) return False, info # -------------------获取文件名------------------- # header里有文件名,则使用它,否则在url里截取,但不能保证url包含文件名 if 'Content-disposition' in r.headers: file_name = r.headers['Content-disposition'].split('"')[1].encode( 'ISO-8859-1').decode('utf-8') elif os_PATH.basename(file_url): file_name = os_PATH.basename(file_url).split("?")[0] else: file_name = f'untitled_{time()}_{randint(0, 100)}' file_name = re_SUB(r'[\\/*:|<>?"]', '', file_name).strip() if rename: # 重命名文件,不改变扩展名 rename = re_SUB(r'[\\/*:|<>?"]', '', rename).strip() ext_name = file_name.split('.')[-1] if rename.lower().endswith( f'.{ext_name}'.lower()) or ext_name == file_name: full_name = rename else: full_name = f'{rename}.{ext_name}' else: full_name = file_name goal_Path = Path(goal_path) goal_path = '' for key, i in enumerate(goal_Path.parts): # 去除路径中的非法字符 goal_path += goal_Path.drive if key == 0 and goal_Path.drive else re_SUB( r'[*:|<>?"]', '', i).strip() goal_path += '\\' if i != '\\' and key < len( goal_Path.parts) - 1 else '' goal_Path = Path(goal_path) goal_Path.mkdir(parents=True, exist_ok=True) goal_path = goal_Path.absolute() full_path = Path(f'{goal_path}\\{full_name}') if full_path.exists(): if file_exists == 'skip': return False, 'A file with the same name already exists.' elif file_exists == 'overwrite': pass elif file_exists == 'rename': full_name = get_available_file_name(goal_path, full_name) full_path = Path(f'{goal_path}\\{full_name}') else: raise ValueError( "Argument file_exists can only be 'skip', 'overwrite', 'rename'." ) if show_msg: # 打印要下载的文件 print(full_name if file_name == full_name else f'{file_name} -> {full_name}') print(f'Downloading to: {goal_path}') # -------------------开始下载------------------- # 获取远程文件大小 file_size = int(r.headers['Content-Length'] ) if 'Content-Length' in r.headers else None downloaded_size, download_status = 0, False # 已下载文件大小和下载状态 try: with open(str(full_path), 'wb') as tmpFile: for chunk in r.iter_content(chunk_size=1024): if chunk: tmpFile.write(chunk) if show_msg and file_size: # 如表头有返回文件大小,显示进度 downloaded_size += 1024 rate = downloaded_size / file_size if downloaded_size < file_size else 1 print('\r {:.0%} '.format(rate), end="") except Exception as e: download_status, info = False, f'Download failed.\n{e}' else: download_status, info = ( False, 'File size is 0.') if full_path.stat().st_size == 0 else ( True, 'Success.') finally: # 删除下载出错文件 if not download_status and full_path.exists(): full_path.unlink() r.close() # -------------------显示并返回值------------------- if show_msg: print(info) info = f'{goal_path}\\{full_name}' if download_status else info return download_status, info
def download(self, file_url: str, goal_path: str = None, rename: str = None, file_exists: str = 'rename', post_data: dict = None, show_msg: bool = False, show_errmsg: bool = False, **kwargs) -> tuple: """下载一个文件 \n :param file_url: 文件url :param goal_path: 存放路径 :param rename: 重命名文件,可不写扩展名 :param file_exists: 若存在同名文件,可选择 'rename', 'overwrite', 'skip' 方式处理 :param show_msg: 是否显示下载信息 :param show_errmsg: 是否抛出和显示异常 :param kwargs: 连接参数 :return: 下载是否成功(bool)和状态信息(成功时信息为文件路径)的元组 """ # 生成的response不写入self._response,是临时的 goal_path = goal_path or OptionsManager().get_value( 'paths', 'global_tmp_path') if not goal_path: raise IOError('No path specified.') kwargs['stream'] = True if 'timeout' not in kwargs: kwargs['timeout'] = 20 if not post_data: r, info = self._make_response(file_url, mode='get', show_errmsg=show_errmsg, **kwargs) else: r, info = self._make_response(file_url, mode='post', data=post_data, show_errmsg=show_errmsg, **kwargs) if r is None: if show_msg: print(info) return False, info if not r.ok: if show_errmsg: raise ConnectionError(f'Status code: {r.status_code}.') return False, f'Status code: {r.status_code}.' # -------------------获取文件名------------------- file_name = '' content_disposition = tuple(x for x in r.headers if x.lower() == 'content-disposition') if content_disposition: # header里有文件名,则使用它 file_name = r.headers[content_disposition[0]].encode( 'ISO-8859-1').decode('utf-8') file_name = re.search(r'filename *= *"?([^";]+)', file_name) if file_name: file_name = file_name.group(1) if file_name[0] == file_name[-1] == "'": file_name = file_name.strip("'") if not file_name and os_PATH.basename(file_url): # 在url里获取文件名 file_name = os_PATH.basename(file_url).split("?")[0] if not file_name: # 找不到则用时间和随机数生成文件名 file_name = f'untitled_{time()}_{randint(0, 100)}' file_name = re_SUB(r'[\\/*:|<>?"]', '', file_name).strip() # 去除非法字符 file_name = parse.unquote(file_name) # -------------------重命名文件名------------------- if rename: # 重命名文件,不改变扩展名 rename = re_SUB(r'[\\/*:|<>?"]', '', rename).strip() ext_name = file_name.split('.')[-1] if '.' in rename or ext_name == file_name: full_name = rename else: full_name = f'{rename}.{ext_name}' else: full_name = file_name # -------------------生成路径------------------- goal_Path = Path(goal_path) goal_path = '' for key, i in enumerate(goal_Path.parts): # 去除路径中的非法字符 goal_path += goal_Path.drive if key == 0 and goal_Path.drive else re_SUB( r'[*:|<>?"]', '', i).strip() goal_path += '\\' if i != '\\' and key < len( goal_Path.parts) - 1 else '' goal_Path = Path(goal_path) goal_Path.mkdir(parents=True, exist_ok=True) goal_path = goal_Path.absolute() full_path = Path(f'{goal_path}\\{full_name}') if full_path.exists(): if file_exists == 'skip': return False, 'A file with the same name already exists.' elif file_exists == 'overwrite': pass elif file_exists == 'rename': full_name = get_available_file_name(goal_path, full_name) full_path = Path(f'{goal_path}\\{full_name}') else: raise ValueError( "Argument file_exists can only be 'skip', 'overwrite', 'rename'." ) # -------------------打印要下载的文件------------------- if show_msg: print(full_name if file_name == full_name else f'{file_name} -> {full_name}') print(f'Downloading to: {goal_path}') # -------------------开始下载------------------- # 获取远程文件大小 content_length = tuple(x for x in r.headers if x.lower() == 'content-length') file_size = int(r.headers[content_length]) if content_length else None downloaded_size, download_status = 0, False # 已下载文件大小和下载状态 try: with open(str(full_path), 'wb') as tmpFile: for chunk in r.iter_content(chunk_size=1024): if chunk: tmpFile.write(chunk) if show_msg and file_size: # 如表头有返回文件大小,显示进度 downloaded_size += 1024 rate = downloaded_size / file_size if downloaded_size < file_size else 1 print('\r {:.0%} '.format(rate), end="") except Exception as e: if show_errmsg: raise ConnectionError(e) download_status, info = False, f'Download failed.\n{e}' else: if full_path.stat().st_size == 0: if show_errmsg: raise ValueError('File size is 0.') download_status, info = False, 'File size is 0.' else: download_status, info = True, 'Success.' finally: if not download_status and full_path.exists(): full_path.unlink() # 删除下载出错文件 r.close() # -------------------显示并返回值------------------- if show_msg: print(info) info = f'{goal_path}\\{full_name}' if download_status else info return download_status, info
def do(url: str, goal: str, new_name: str = None, exists: str = 'rename', data: dict = None, msg: bool = False, errmsg: bool = False, **args) -> tuple: args['stream'] = True if 'timeout' not in args: args['timeout'] = 20 mode = 'post' if data else 'get' # 生成的response不写入self._response,是临时的 r, info = self._make_response(url, mode=mode, data=data, show_errmsg=errmsg, **args) if r is None: if msg: print(info) return False, info if not r.ok: if errmsg: raise ConnectionError(f'Status code: {r.status_code}.') return False, f'Status code: {r.status_code}.' # -------------------获取文件名------------------- file_name = '' content_disposition = r.headers.get('content-disposition') # 使用header里的文件名 if content_disposition: file_name = r.headers[content_disposition[0]].encode( 'ISO-8859-1').decode('utf-8') file_name = re.search(r'filename *= *"?([^";]+)', file_name) if file_name: file_name = file_name.group(1) if file_name[0] == file_name[-1] == "'": file_name = file_name[1:-1] # 在url里获取文件名 if not file_name and os_PATH.basename(url): file_name = os_PATH.basename(url).split("?")[0] # 找不到则用时间和随机数生成文件名 if not file_name: file_name = f'untitled_{time()}_{randint(0, 100)}' # 去除非法字符 file_name = re_SUB(r'[\\/*:|<>?"]', '', file_name).strip() file_name = unquote(file_name) # -------------------重命名,不改变扩展名------------------- if new_name: new_name = re_SUB(r'[\\/*:|<>?"]', '', new_name).strip() ext_name = file_name.split('.')[-1] if '.' in new_name or ext_name == file_name: full_name = new_name else: full_name = f'{new_name}.{ext_name}' else: full_name = file_name # -------------------生成路径------------------- goal_Path = Path(goal) goal = '' skip = False for key, i in enumerate(goal_Path.parts): # 去除路径中的非法字符 goal += goal_Path.drive if key == 0 and goal_Path.drive else re_SUB( r'[*:|<>?"]', '', i).strip() goal += '\\' if i != '\\' and key < len( goal_Path.parts) - 1 else '' goal_Path = Path(goal).absolute() goal_Path.mkdir(parents=True, exist_ok=True) full_path = Path(f'{goal}\\{full_name}') if full_path.exists(): if file_exists == 'rename': full_name = get_available_file_name(goal, full_name) full_path = Path(f'{goal}\\{full_name}') elif exists == 'skip': skip = True elif exists == 'overwrite': pass else: raise ValueError( "Argument file_exists can only be 'skip', 'overwrite', 'rename'." ) # -------------------打印要下载的文件------------------- if msg: print(file_url) print(full_name if file_name == full_name else f'{file_name} -> {full_name}') print(f'Downloading to: {goal}') if skip: print('Skipped.\n') # -------------------开始下载------------------- if skip: return False, 'Skipped because a file with the same name already exists.' # 获取远程文件大小 content_length = r.headers.get('content-length') file_size = int(content_length) if content_length else None # 已下载文件大小和下载状态 downloaded_size, download_status = 0, False try: with open(str(full_path), 'wb') as tmpFile: for chunk in r.iter_content(chunk_size=1024): if chunk: tmpFile.write(chunk) # 如表头有返回文件大小,显示进度 if msg and file_size: downloaded_size += 1024 rate = downloaded_size / file_size if downloaded_size < file_size else 1 print('\r {:.0%} '.format(rate), end="") except Exception as e: if errmsg: raise ConnectionError(e) download_status, info = False, f'Download failed.\n{e}' else: if full_path.stat().st_size == 0: if errmsg: raise ValueError('File size is 0.') download_status, info = False, 'File size is 0.' else: download_status, info = True, str(full_path) finally: # 删除下载出错文件 if not download_status and full_path.exists(): full_path.unlink() r.close() # -------------------显示并返回值------------------- if msg: print(info, '\n') info = f'{goal}\\{full_name}' if download_status else info return download_status, info