zipfile 模块本身在添加文件时,ZIP 文件中已存在同名文件,默认行为是覆盖,但“覆盖”这个词在不同场景下有不同的含义,我们需要区分清楚。

向现有的 ZIP 文件添加/更新文件(最常见)
这是最典型的“覆盖”需求,你有一个已经存在的 archive.zip,里面有一些文件,现在你想要向它添加一个新文件 new_file.txt,或者更新一个已存在的同名文件 old_file.txt。
方法 1:追加模式 'a' (推荐)
这是最直接、最高效的方法。'a' (append) 模式会打开一个现有的 ZIP 文件,并向其追加新文件,ZIP 文件中已存在同名文件,新文件会自动覆盖旧文件。
优点:
- 高效:它不需要解压和重新压缩整个 ZIP 文件,它只是在 ZIP 文件的末尾添加新的文件记录。
示例代码:

import zipfile
import os
# 准备一些文件
with open('file_to_add.txt', 'w') as f:
f.write('这是新添加的内容。')
with open('file_to_update.txt', 'w') as f:
f.write('这是更新后的内容。')
# 创建一个初始的 ZIP 文件
with zipfile.ZipFile('archive.zip', 'w') as zf:
zf.write('file_to_add.txt', arcname='initial_file.txt')
# archive.zip 中只有 initial_file.txt
print("初始 ZIP 文件创建完成。")
# --- 覆盖操作 ---
# 使用 'a' 模式打开现有的 ZIP 文件
with zipfile.ZipFile('archive.zip', 'a') as zf:
# 添加一个新文件
zf.write('file_to_add.txt', arcname='new_file.txt')
print("已添加 new_file.txt")
# 更新一个已存在的文件
zf.write('file_to_update.txt', arcname='initial_file.txt')
print("已覆盖 initial_file.txt")
# 验证结果
with zipfile.ZipFile('archive.zip', 'r') as zf:
print("\n当前 ZIP 文件内容:")
zf.printdir()
# 读取被覆盖的文件内容
print("\n被覆盖的文件 'initial_file.txt' 内容:")
print(zf.read('initial_file.txt').decode('utf-8'))
# 清理
os.remove('file_to_add.txt')
os.remove('file_to_update.txt')
输出:
初始 ZIP 文件创建完成。
已添加 new_file.txt
已覆盖 initial_file.txt
当前 ZIP 文件内容:
File Name Modified Size
initial_file.txt 2025-10-27 10:30:00 27
new_file.txt 2025-10-27 10:30:00 27
被覆盖的文件 'initial_file.txt' 内容:
这是更新后的内容。
方法 2:读取-解压-修改-重新打包 (不推荐,除非有特殊需求)
这种方法效率较低,因为它需要将整个 ZIP 文件解压到一个临时目录,进行修改,然后再重新打包成一个新的 ZIP 文件。它无法直接在原 ZIP 文件上进行“覆盖”。
什么时候需要用这种方法?
- 当你需要修改 ZIP 文件中已存在文件的内容,而不是简单地用一个新文件替换它时。
zipfile.write()只能添加完整的新文件,不能直接修改 ZIP 内部文件的流。
示例代码 (演示低效的“覆盖”):
import zipfile
import os
import shutil
# 假设我们有一个 archive.zip
if not os.path.exists('archive.zip'):
# 如果不存在,先创建一个
with zipfile.ZipFile('archive.zip', 'w') as zf:
zf.writestr('file_in_zip.txt', '原始内容')
print("原始 ZIP 文件内容:")
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.printdir()
# --- 低效的覆盖过程 ---
# 1. 创建一个临时目录
temp_dir = 'temp_unzip'
os.makedirs(temp_dir, exist_ok=True)
# 2. 解压 ZIP 文件
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extractall(temp_dir)
# 3. 修改解压后的文件
file_path = os.path.join(temp_dir, 'file_in_zip.txt')
with open(file_path, 'w') as f:
f.write('这是通过解压-修改-重写更新的内容。')
# 4. 重新打包成一个新的 ZIP 文件
# 注意:这里我们创建了一个新文件 'archive_new.zip'
with zipfile.ZipFile('archive_new.zip', 'w') as zf:
for root, dirs, files in os.walk(temp_dir):
for file in files:
file_path = os.path.join(root, file)
# arcname 确保在 ZIP 中的路径是干净的
arcname = os.path.relpath(file_path, temp_dir)
zf.write(file_path, arcname=arcname)
# 5. (可选) 用新文件替换旧文件
# os.replace('archive_new.zip', 'archive.zip') # 如果你希望原地替换
# 清理
shutil.rmtree(temp_dir)
print("\n新的 ZIP 文件内容:")
with zipfile.ZipFile('archive_new.zip', 'r') as zf:
zf.printdir()
print("\n文件内容:")
print(zf.read('file_in_zip.txt').decode('utf-8'))
# 清理
os.remove('archive_new.zip')
创建新的 ZIP 文件时覆盖旧文件
当你使用 'w' (write) 模式打开一个 ZIP 文件时,如果该文件已存在,它会被立即截断并清空,相当于被完全覆盖。
示例代码:
import zipfile
# 假设 archive.zip 已存在
with zipfile.ZipFile('archive.zip', 'w') as zf:
zf.writestr('hello.txt', '你好,世界!')
print("使用 'w' 模式后,archive.zip 被完全重写。")
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.printdir()
运行这段代码后,原来 archive.zip 里的所有文件都会消失,只剩下新创建的 hello.txt。
如何避免覆盖?检查文件是否存在
有时候你不想覆盖,只想在文件不存在时才添加,你可以先检查 ZIP 文件中是否已包含该文件。
示例代码:
import zipfile
def add_if_not_exists(zip_name, filename_in_zip, file_to_add_path):
with zipfile.ZipFile(zip_name, 'a') as zf:
if filename_in_zip not in zf.namelist():
zf.write(file_to_add_path, arcname=filename_in_zip)
print(f"已添加 {filename_in_zip}")
else:
print(f"{filename_in_zip} 已存在,未添加。")
# 创建一个测试 ZIP
with zipfile.ZipFile('no_overwrite.zip', 'w') as zf:
zf.writestr('existing.txt', '请不要覆盖我')
# 尝试添加一个已存在的文件和一个新文件
add_if_not_exists('no_overwrite.zip', 'existing.txt', 'some_other_file.txt')
add_if_not_exists('no_overwrite.zip', 'new_file.txt', 'some_other_file.txt')
# 验证
with zipfile.ZipFile('no_overwrite.zip', 'r') as zf:
zf.printdir()
总结与最佳实践
| 场景 | 推荐方法 | 模式 | 行为 |
|---|---|---|---|
| 向现有 ZIP 添加/更新文件 | zipfile.ZipFile(file, 'a') |
'a' (append) |
高效,直接在文件末尾追加,同名文件自动覆盖。这是首选。 |
| 创建全新的 ZIP 文件 | zipfile.ZipFile(file, 'w') |
'w' (write) |
如果文件存在,则完全覆盖(清空后重建)。 |
| 需要修改 ZIP 内部文件内容 | 读取-解压-修改-重新打包 | 无直接模式 | 低效,适用于复杂操作,不能直接在原文件上修改。 |
| 避免添加同名文件 | 先检查 filename in zf.namelist() |
'a' |
在追加模式下,先检查文件是否已存在,再决定是否添加。 |
核心要点:
- 追加模式
'a'是处理现有 ZIP 文件更新的最佳方式,因为它高效且能自然地处理覆盖。 - 写入模式
'w'会销毁并重建 ZIP 文件,请谨慎使用,除非你就是要创建一个全新的文件。 zf.namelist()是检查 ZIP 文件内文件列表的好方法,可用于实现“避免覆盖”的逻辑。
