在日常开发中,文件打包是个绕不开的活。我见过不少新手一遇到需要打包文件的任务,就急着去找第三方库,其实Python标准库里的shutil.make_archive()已经能解决90%的问题。这个函数就像个瑞士军刀,简单几行代码就能把文件夹变成zip、tar.gz等各种格式的压缩包。
上周我帮同事处理一个自动化部署脚本,原本他们用subprocess调系统命令打包,不仅代码臃肿,还在Windows服务器上报错。换成shutil.make_archive()后,代码量减少了三分之二,而且跨平台运行稳如老狗。这就是标准库的魅力——不用装额外依赖,写出来的代码又干净又可靠。
先看个最简单的例子,把当前目录下的docs文件夹打包成zip:
python复制import shutil
shutil.make_archive('docs_backup', 'zip', '.', 'docs')
这行代码干了三件事:
实测发现,如果docs_backup.zip已存在,函数会直接覆盖而不警告。所以重要文件记得先备份,这个坑我踩过。
base_name:
format:
常用格式对比:
| 格式 | 压缩率 | 速度 | 适用场景 |
|---|---|---|---|
| zip | 中 | 快 | Windows环境 |
| gztar | 较高 | 中 | Linux日志打包 |
| bztar | 最高 | 慢 | 需要极致压缩率 |
注意:bztar在某些老旧系统可能不支持
root_dir:
base_dir:
假设有这样的目录:
code复制/project
/src
__init__.py
utils.py
/tests
test_utils.py
README.md
只想打包src和README.md怎么办?可以这样操作:
python复制import os
from tempfile import mkdtemp
# 创建临时目录组织文件结构
tmp_dir = mkdtemp()
os.makedirs(os.path.join(tmp_dir, 'project'))
os.symlink('/project/src', os.path.join(tmp_dir, 'project/src'))
os.symlink('/project/README.md', os.path.join(tmp_dir, 'project/README.md'))
# 打包临时目录
shutil.make_archive(
base_name='/output/project',
format='zip',
root_dir=tmp_dir,
base_dir='project'
)
这个技巧在Docker镜像构建时特别有用,可以精确控制打包内容。
结合datetime自动生成带时间戳的包名:
python复制from datetime import datetime
timestamp = datetime.now().strftime('%Y%m%d_%H%M')
shutil.make_archive(
f'backup_{timestamp}',
'gztar',
'/var/log/myapp'
)
我在自动化部署脚本里常用这招,配合日志轮转美滋滋。
当处理GB级日志文件时,直接打包可能内存爆炸。这时可以用生成器配合低级API:
python复制def big_file_handler():
for root, dirs, files in os.walk('/var/log/bigdata'):
for file in files:
yield os.path.join(root, file)
with tarfile.open('bigdata.tar.gz', 'w:gz') as tar:
for filepath in big_file_handler():
tar.add(filepath, arcname=os.path.basename(filepath))
虽然代码变复杂了,但内存占用从2GB降到了200MB左右。
OSError: [Errno 13] Permission denied
chmod 777 /outputFileNotFoundError
LargeZipFile(zip格式特有)
去年我开发过一个Web应用的自动部署系统,核心打包代码如下:
python复制def package_webapp(version, env='prod'):
build_dir = f'/tmp/build_{version}'
os.makedirs(build_dir, exist_ok=True)
# 复制必要文件
shutil.copytree('./static', f'{build_dir}/static')
shutil.copy2('app.py', build_dir)
# 根据环境选择配置文件
config_file = 'config_prod.json' if env == 'prod' else 'config_dev.json'
shutil.copy2(config_file, f'{build_dir}/config.json')
# 生成压缩包
archive_path = shutil.make_archive(
base_name=f'/deployments/webapp_{version}_{env}',
format='gztar',
root_dir=build_dir
)
# 清理临时文件
shutil.rmtree(build_dir)
return archive_path
这个方案在20多个微服务中稳定运行至今,关键点在于:
虽然shutil.make_archive()很强大,但有些场景可能需要更专业的工具:
需要加密压缩时
python复制import pyzipper
with pyzipper.AESZipFile('secret.zip', 'w', encryption=pyzipper.WZ_AES) as zf:
zf.setpassword(b"password")
zf.write('secret.txt')
需要分卷压缩时
python复制import tarfile
MAX_SIZE = 100 * 1024 * 1024 # 100MB
with tarfile.open('bigdata.tar', 'w') as tar:
tar.add('/data', arcname='')
if os.path.getsize('bigdata.tar') > MAX_SIZE:
split_tar_file() # 自定义分卷逻辑
需要处理特殊属性时
经常遇到打包后文件路径不对的情况,我总结了个检查清单:
python复制shutil.make_archive('test', 'zip', '.', dry_run=True)
# 会打印操作日志但不实际执行
bash复制unzip -l test.zip # 查看zip内容
tar -tf test.tar.gz # 查看tar内容
最后分享一个真实踩坑案例:有次自动化脚本打包node_modules目录,50万个小文件直接把服务器IO打满。后来改成先检查文件数量,超过1万个就警告确认。所以记住——打包操作不是无害的,生产环境执行前要多加小心。