如何用Python比较多卷7Z归档文件夹,找出超1GB差异内容?
问题:对比多卷7Z归档中的文件/文件夹大小变化
我有两个路径,各自存放着多卷.7Z归档文件:
- 路径A包含4个文件:
example1.7z.001, example1.7z.002, example1.7z.003, example1.7z.004(总大小15GB),解压后得到一个20GB的7z文件,再次解压得到40GB的文件夹,其中TEST1文件夹占5GB。 - 路径B包含5个文件:
example2.7z.001, example2.7z.002, example2.7z.003, example2.7z.004, example2.7z.005(总大小20GB),解压后得到22GB的7z文件,再次解压得到50GB的文件夹,其中TEST1文件夹增至7GB,新增TEST2文件夹占1.2GB。
需求:编写Python脚本,输入这两个路径后,输出大小增量超1GB的现有文件/文件夹,或大小超1GB的新增文件/文件夹(本例应返回TEST1和TEST2)。
初步调研的三种方案
1. 使用magic库
magic库仅能检测文件类型、判断两个7Z文件内容是否完全一致,无法获取归档内部文件/文件夹的大小信息,完全满足不了需求,直接排除。
代码示例:
import magic def compare_7zip_content(file1, file2): with open(file1, 'rb') as f1, open(file2, 'rb') as f2: m1 = magic.Magic(mime=True) m2 = magic.Magic(mime=True) file1_content = f1.read() file2_content = f2.read() file1_type = m1.from_buffer(file1_content) file2_type = m2.from_buffer(file2_content) if file1_type == 'application/x-7z-compressed' and file2_type == 'application/x-7z-compressed': if file1_content == file2_content: return True else: return False else: raise ValueError('One or both of the files are not 7zip format')
2. 使用py7zr库
py7zr是Python原生的7Z处理库,支持读取多卷归档的内容信息,无需完全解压文件,效率较高。但你提供的代码存在几个问题:
- 仅处理单文件7Z,未考虑
.7z.001这类多卷归档的情况 - 直接按顺序zip两个归档的文件列表,会漏掉新增/删除的文件
- 只计算单个文件的大小差,未处理文件夹的总大小统计
代码示例:
import py7zr import os folder1 = '/path/to/folder1' folder2 = '/path/to/folder2' for filename in os.listdir(folder1): if filename.endswith('.7z'): file1 = os.path.join(folder1, filename) file2 = os.path.join(folder2, filename) with py7zr.SevenZipFile(file1, mode='r') as archive1, \ py7zr.SevenZipFile(file2, mode='r') as archive2: archive1_files = archive1.getnames() archive2_files = archive2.getnames() for archive1_file, archive2_file in zip(archive1_files, archive2_files): size_diff = abs(archive1.getmember(archive1_file).file_size - archive2.getmember(archive2_file).file_size) if size_diff >= 1000000000: # 1 GB in bytes print(f"{filename}: {size_diff / 1000000000} GB")
3. 使用7z CLI工具
通过调用系统的7z命令行工具,执行l(列表)命令获取归档内的文件/文件夹信息,再解析输出结果。优点是兼容性好,支持所有7Z特性;缺点是需要系统提前安装7z工具,且解析命令输出需要处理字符串,稳定性依赖于7z的输出格式。
推荐方案及优化思路
优先选择py7zr库,因为它是Python原生库,不需要依赖外部工具,代码可移植性更强。针对需求,需要对原有代码进行以下优化:
优化点说明
- 处理多卷归档:py7zr支持自动识别多卷,只需传入分卷的第一个文件(比如
example1.7z.001)即可 - 统计文件夹总大小:归档中的文件夹本身无大小,需递归累加其下所有文件的大小总和
- 完整对比文件集合:既要对比共有项的大小增量,也要检查新增项的大小是否达标
优化后的示例代码
import py7zr import os from collections import defaultdict GB = 1024 * 1024 * 1024 # 二进制1GB,若需十进制统计可改为10**9 def get_archive_size_map(archive_path): """获取归档内所有文件/文件夹的大小映射,文件夹值为下属文件总大小""" size_map = defaultdict(int) with py7zr.SevenZipFile(archive_path, mode='r') as archive: for entry in archive.list(): if entry.is_dir: continue # 累加文件本身大小 size_map[entry.filename] += entry.file_size # 递归累加所有上级文件夹的大小 parts = entry.filename.split('/') for i in range(1, len(parts)): folder_path = '/'.join(parts[:i]) size_map[folder_path] += entry.file_size return size_map def compare_archives(path_a, path_b): # 获取路径下的多卷7Z主文件(.7z.001) def get_main_archive(folder): for filename in os.listdir(folder): if filename.endswith('.7z.001'): return os.path.join(folder, filename) raise FileNotFoundError(f"路径{folder}中未找到多卷7Z主文件(.7z.001)") archive_a = get_main_archive(path_a) archive_b = get_main_archive(path_b) size_map_a = get_archive_size_map(archive_a) size_map_b = get_archive_size_map(archive_b) result = [] # 检查共有项的大小增量 for name in size_map_a: if name in size_map_b: diff = size_map_b[name] - size_map_a[name] if diff > GB: result.append(f"{name}(增量:{diff/GB:.2f} GB)") # 检查新增项的大小 for name in size_map_b: if name not in size_map_a: if size_map_b[name] > GB: result.append(f"{name}(新增,大小:{size_map_b[name]/GB:.2f} GB)") return result # 示例调用 if __name__ == "__main__": path_a = "/path/to/folderA" path_b = "/path/to/folderB" changes = compare_archives(path_a, path_b) print("符合条件的文件/文件夹:") for item in changes: print(f"- {item}")
备选方案:7z CLI实现
如果py7zr遇到兼容性问题(比如特殊压缩算法),可以使用7z CLI,示例代码如下:
import subprocess import os from collections import defaultdict GB = 1024 * 1024 * 1024 def get_archive_size_map_cli(archive_path): size_map = defaultdict(int) # 执行7z l命令获取详细文件列表 result = subprocess.run( ["7z", "l", "-slt", archive_path], capture_output=True, text=True, check=True ) # 解析输出内容 current_file = None current_size = 0 for line in result.stdout.splitlines(): if line.startswith("Path = "): current_file = line.split(" = ")[1] elif line.startswith("Size = "): current_size = int(line.split(" = ")[1]) if current_file: # 累加文件和上级文件夹大小 parts = current_file.split('/') for i in range(1, len(parts)): folder_path = '/'.join(parts[:i]) size_map[folder_path] += current_size size_map[current_file] += current_size current_file = None return size_map # 对比逻辑复用py7zr版本的compare_archives函数即可
方案选择总结
- 优先选py7zr:Python原生实现,无需外部依赖,代码可控性强,适合大多数场景
- 次选7z CLI:兼容性拉满,但需要系统安装7z工具,解析输出易受版本影响
- 排除magic库:仅能做文件类型校验和内容一致性对比,完全满足不了大小统计需求
内容的提问来源于stack exchange,提问作者arielma
相关产品推荐
相关产品推荐

