Python 3.7批量压缩指定日期旧.log文件时大文件量异常问题
问题分析与修复方案
我来帮你排查并解决这个批量归档log文件的问题——在处理10万级文件量时出现的“所有文件塞进单个Zip包”的情况,根源出在几个容易被忽略的代码细节上:
1. 未定义变量引发的异常连锁反应
原代码里注释掉了x = os.stat(i).st_mtime,但后续却用x来转换文件日期,这会直接抛出NameError异常。当程序遇到这个未捕获的异常后,后续的文件处理逻辑会混乱,比如复用之前未关闭的ZipFile对象,导致所有文件错误地写入同一个压缩包。
2. ZipFile对象的作用域与未关闭的文件句柄
你的代码只在创建新Zip包时初始化zip对象,但处理已存在的Zip包时,直接使用之前的zip变量(这个变量可能指向完全不同日期的ZipFile对象)。而且全程没有关闭ZipFile对象,这会导致:
- 文件句柄泄漏,处理大量文件时可能触发系统文件句柄上限
- 压缩包数据写入不完整甚至损坏
- 后续文件错误地写入到之前未关闭的Zip包中,也就是你看到的“所有文件进单个Zip”的问题
3. 大文件量下的遍历效率问题(可选优化)
用os.listdir()遍历所有文件再逐个判断,在10万级文件量下效率较低,改用os.scandir()可以直接获取文件的stat信息,减少系统调用次数。
修复后的完整代码
import os import datetime import time from zipfile import ZIP_DEFLATED, ZipFile import zipfile def main(): # 计算截止日期:当前日期往前推2天 today = datetime.datetime.today() delta = datetime.timedelta(days=2) cutoff_date = (today - delta).date() print(f"{cutoff_date} 日期之前的.log文件将被归档压缩") cutoff_timestamp = time.mktime(cutoff_date.timetuple()) # 用字典保存已打开的ZipFile对象,避免重复打开/创建,确保每个日期对应正确的压缩包 open_zips = {} try: # 使用os.scandir提升大文件量下的遍历效率 with os.scandir() as entries: for entry in entries: # 跳过目录,只处理.log后缀的文件 if not entry.is_file() or not entry.name.endswith(".log"): continue # 判断文件修改时间是否早于截止日期 if entry.stat().st_mtime < cutoff_timestamp: file_mtime = entry.stat().st_mtime file_date = datetime.datetime.fromtimestamp(file_mtime).date() zip_name = f"{file_date}.zip" print(f"正在处理文件: {entry.name}") # 如果该日期的Zip包未打开,创建或打开它 if zip_name not in open_zips: # 尝试使用压缩模式,失败则用存储模式 try: import zlib mode = ZIP_DEFLATED except ImportError: mode = ZIP_STORED # 已存在的Zip包用追加模式,新包用写入模式 zip_mode = 'a' if os.path.exists(zip_name) else 'w' open_zips[zip_name] = ZipFile(zip_name, zip_mode, mode) print(f"{zip_name} 已准备完成") # 将文件写入对应日期的Zip包 open_zips[zip_name].write(entry.name) print(f"{entry.name} 已添加到 {zip_name}") os.remove(entry.name) time.sleep(0.1) finally: # 确保所有打开的Zip包都被关闭,避免文件句柄泄漏和数据丢失 for zip_file in open_zips.values(): zip_file.close() print(f"{zip_file.filename} 已安全关闭") print("所有归档操作已完成!") if __name__ == "__main__": main()
关键修复点说明
- 修复未定义变量问题:直接使用
entry.stat().st_mtime获取文件修改时间,避免了未定义变量引发的异常 - 用字典管理ZipFile对象:确保每个日期的文件都写入对应日期的Zip包,不会出现交叉写入的情况
- 显式关闭ZipFile对象:在
finally块中统一关闭所有压缩包,彻底解决文件句柄泄漏和数据写入不完整的问题 - 使用
os.scandir()提升效率:在大量文件场景下,比os.listdir()减少了重复的stat系统调用,运行更快 - 追加模式打开已存在的Zip包:原代码中复用旧Zip对象的逻辑被替换为正确的追加模式,确保已存在的Zip包能正确接收新文件
内容的提问来源于stack exchange,提问作者Metin Kahraman
相关产品推荐
相关产品推荐

