# +====================================================================== # | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] # +---------------------------------------------------------------------- # | Copyright (c) 2021-2026 珊瑚云团队 版权所有 # +---------------------------------------------------------------------- # | 开源协议: Apache-2.0 # | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。 # +---------------------------------------------------------------------- # | 官方网站: https://www.djangoadmin.cn # +---------------------------------------------------------------------- # | 研发团队: 珊瑚云团队 荣誉出品 # +====================================================================== # | 版权和免责声明: # | 一、知识产权声明 # | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、 # | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权, # | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。 # | 二、使用限制与禁止条款 # | 您理解并同意,禁止将本产品用于以下任何行为: # | 1. 任何违反中华人民共和国法律法规的用途; # | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为; # | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。 # | 三、责任豁免 # | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失 # | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三 # | 方提起索赔或行政机关进行调查,您应独立承担全部责任。 # | 四、违约与法律责任 # | 如发现任何违反本声明的行为,本团队有权: # | 1. 无条件配合公安机关及其他有权机关的调查取证工作; # | 2. 依法追究相关方的民事责任,并要求赔偿全部损失; # | 3. 保留进一步采取刑事控告等法律措施的权利。 # | 五、许可范围说明 # | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包 # | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通 # | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、 # | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造 # | 成的一切经济损失及商誉损害。 # | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件 # | 许可协议》附件为准。 # +====================================================================== """ 文件目录操作工具模块 ============================================================= 该模块提供文件和目录操作的常用工具函数。 主要用于处理文件上传、目录创建、路径管理等场景,确保文件系统操作的 安全性和可靠性。包含目录创建、文件操作等基础功能。 主要功能: 1. 递归创建目录(mkdir)- 确保目标目录存在 2. 文件路径处理 3. 目录存在性检查 所属包: utils 所在文件: utils/file_utils.py 依赖模块: os - 操作系统接口模块 """ import logging import os logger = logging.getLogger(__name__) # 常见文件类型的魔术字节签名 # 格式:扩展名 -> [签名列表],签名为 (offset, bytes) 元组 MAGIC_SIGNATURES = { # 图片 '.jpg': [(0, b'\xff\xd8\xff')], '.jpeg': [(0, b'\xff\xd8\xff')], '.png': [(0, b'\x89PNG\r\n\x1a\n')], '.gif': [(0, b'GIF87a'), (0, b'GIF89a')], '.bmp': [(0, b'BM')], '.webp': [(0, b'RIFF')], '.ico': [(0, b'\x00\x00\x01\x00'), (0, b'\x00\x00\x02\x00')], # 文档 '.pdf': [(0, b'%PDF')], '.zip': [(0, b'PK\x03\x04')], '.rar': [(0, b'Rar!\x1a\x07')], '.7z': [(0, b'7z\xbc\xaf\x27\x1c')], '.mp3': [(0, b'\xff\xfb'), (0, b'\xff\xf3'), (0, b'\xff\xf2'), (0, b'ID3')], '.mp4': [(4, b'ftyp')], # SVG 是文本格式,无固定二进制签名 } def verify_file_magic(file_stream, ext_with_dot): """ 校验文件魔术字节,验证文件实际内容与扩展名是否一致 Args: file_stream: Flask上传文件对象(FileStorage),需支持seek/read ext_with_dot: 带点的扩展名,如 '.jpg' Returns: tuple: (is_valid, message) - is_valid (bool): 是否通过校验 - message (str): 提示信息 """ signatures = MAGIC_SIGNATURES.get(ext_with_dot) if not signatures: # 无签名规则的文件类型,跳过魔术字节校验 return True, '' try: # 读取文件头部字节用于校验 header = file_stream.read(32) file_stream.seek(0) if not header: return False, '文件内容为空' for sig in signatures: if isinstance(sig, tuple): offset, sig_bytes = sig if header[offset:offset + len(sig_bytes)] == sig_bytes: return True, '' elif header.startswith(sig): return True, '' return False, f'文件内容与扩展名 {ext_with_dot} 不匹配' except Exception as e: logger.error(f'魔术字节校验异常: {e}') # 读取异常时不阻断上传,降级为仅扩展名校验 return True, '' def mkdir(path): """ 递归创建目录 ============================================================= 检查指定路径的目录是否存在,如果不存在则递归创建所有必需的目录。 用于确保文件上传等操作前目标目录已存在。 参数: path (str): 需要创建/检查的目录路径 返回: bool: True - 目录创建成功或已存在 False - 目录创建失败(保留扩展用) 使用示例: >>> # 确保上传目录存在 >>> upload_dir = '/data/uploads/2024/01' >>> mkdir(upload_dir) /data/uploads/2024/01 创建成功 True 注意事项: 1. 函数会自动创建多级不存在的目录 2. 如果目录已存在,不会重复创建 3. 需要有相应的文件系统权限 """ # 去除首位空格 # 说明:消除用户输入中可能包含的前后空格,避免路径错误 path = path.strip() # 去除尾部 \ 符号 # 说明:Windows系统路径可能以\结尾,去除后保持路径格式统一 path = path.rstrip("\\") # 判断路径是否存在 # 函数:os.path.exists(path) # 说明:检查指定路径的文件或目录是否存在 isExists = os.path.exists(path) if not isExists: # 如果不存在则创建目录 # 函数:os.makedirs(path) # 说明:递归创建目录,如果中间目录不存在也会一并创建 # 例如:创建 /a/b/c 时,如果 /a 和 /a/b 都不存在,会一并创建 os.makedirs(path) logger.debug(f'{path} 创建成功') return True else: logger.debug(f'{path} 目录已存在') return False # ======================= 扩展:常用文件操作工具类 ========================== class FileUtils: """ 文件操作工具类 ============================================================= 提供更多常用的文件和目录操作功能,封装为类方法便于调用。 包含文件读写、路径处理、文件信息获取等实用功能。 使用示例: >>> # 获取文件大小 >>> size = FileUtils.get_file_size('/path/file.txt') >>> print(f"{size} bytes") >>> # 安全删除文件 >>> if FileUtils.safe_delete('/path/file.txt'): ... print("文件删除成功") """ @staticmethod def get_file_size(file_path): """ 获取文件大小 ============================================================= 参数: file_path (str): 文件路径 返回: int: 文件大小(字节),如果文件不存在返回0 """ if os.path.exists(file_path) and os.path.isfile(file_path): return os.path.getsize(file_path) return 0 @staticmethod def get_file_extension(filename): """ 获取文件扩展名 ============================================================= 参数: filename (str): 文件名或文件路径 返回: str: 文件扩展名(小写),例如:'.jpg', '.txt' """ import os return os.path.splitext(filename)[1].lower() @staticmethod def get_file_name_without_ext(filename): """ 获取不带扩展名的文件名 ============================================================= 参数: filename (str): 文件名或文件路径 返回: str: 不带扩展名的文件名 """ import os return os.path.splitext(os.path.basename(filename))[0] @staticmethod def safe_delete(file_path): """ 安全删除文件 ============================================================= 参数: file_path (str): 文件路径 返回: bool: True - 删除成功或文件不存在 False - 删除失败(如权限不足) """ try: if os.path.exists(file_path) and os.path.isfile(file_path): os.remove(file_path) return True except Exception as e: logger.warning(f"文件删除失败: {e}") return False @staticmethod def read_file(file_path, encoding='utf-8'): """ 读取文件内容 ============================================================= 参数: file_path (str): 文件路径 encoding (str): 文件编码,默认utf-8 返回: str/None: 文件内容字符串,读取失败返回None """ try: with open(file_path, 'r', encoding=encoding) as f: return f.read() except Exception as e: logger.warning(f"文件读取失败: {e}") return None @staticmethod def write_file(file_path, content, encoding='utf-8', mode='w'): """ 写入文件内容 ============================================================= 参数: file_path (str): 文件路径 content (str): 要写入的内容 encoding (str): 文件编码,默认utf-8 mode (str): 写入模式,'w'覆盖写入,'a'追加写入 返回: bool: True - 写入成功,False - 写入失败 """ try: # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): os.makedirs(directory) with open(file_path, mode, encoding=encoding) as f: f.write(content) return True except Exception as e: logger.warning(f"文件写入失败: {e}") return False @staticmethod def list_files(directory, pattern=None): """ 列出目录下的所有文件 ============================================================= 参数: directory (str): 目录路径 pattern (str): 文件匹配模式,如 '*.txt',None表示所有文件 返回: list: 文件名列表 """ import glob if not os.path.exists(directory): return [] if pattern: search_path = os.path.join(directory, pattern) return [os.path.basename(f) for f in glob.glob(search_path)] else: return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))] @staticmethod def get_file_info(file_path): """ 获取文件详细信息 ============================================================= 参数: file_path (str): 文件路径 返回: dict: 包含文件信息的字典,文件不存在返回None """ if not os.path.exists(file_path) or not os.path.isfile(file_path): return None stat = os.stat(file_path) return { 'name': os.path.basename(file_path), 'path': os.path.abspath(file_path), 'size': stat.st_size, 'size_format': FileUtils._format_size(stat.st_size), 'extension': FileUtils.get_file_extension(file_path), 'created_time': stat.st_ctime, 'modified_time': stat.st_mtime, 'is_hidden': os.path.basename(file_path).startswith('.') } @staticmethod def _format_size(size_bytes): """ 格式化文件大小显示 ============================================================= 参数: size_bytes (int): 文件大小(字节) 返回: str: 格式化后的大小字符串,如 '1.5 MB' """ if size_bytes < 1024: return f"{size_bytes} B" elif size_bytes < 1024 * 1024: return f"{size_bytes / 1024:.2f} KB" elif size_bytes < 1024 * 1024 * 1024: return f"{size_bytes / (1024 * 1024):.2f} MB" else: return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB" # ======================= 便捷函数封装 ========================== def get_file_size(file_path): """获取文件大小(字节)的便捷函数""" return FileUtils.get_file_size(file_path) def safe_delete(file_path): """安全删除文件的便捷函数""" return FileUtils.safe_delete(file_path) def get_file_extension(filename): """获取文件扩展名的便捷函数""" return FileUtils.get_file_extension(filename)