You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:05:34