如何在Python中创建目录结构的zip存档?


当前回答

要保留要归档的父目录下的文件夹层次结构,请执行以下操作:

import glob
import os
import zipfile

with zipfile.ZipFile(fp_zip, "w", zipfile.ZIP_DEFLATED) as zipf:
    for fp in glob(os.path.join(parent, "**/*")):
        base = os.path.commonpath([parent, fp])
        zipf.write(fp, arcname=fp.replace(base, ""))

如果需要,可以将其更改为使用pathlib进行文件globbing。

其他回答

显而易见的方法是使用shutil,就像第二个顶级答案所说的那样,但如果出于某种原因,您仍然希望使用ZipFile,并且如果您在执行此操作时遇到一些问题(如Windows等中的ERR 13),您可以使用此修复程序:

import os
import zipfile

def retrieve_file_paths(dirName):
  filePaths = []
  for root, directories, files in os.walk(dirName):
    for filename in files:
        filePath = os.path.join(root, filename)
        filePaths.append(filePath)
  return filePaths
 
def main(dir_name, output_filename):
  filePaths = retrieve_file_paths(dir_name)
   
  zip_file = zipfile.ZipFile(output_filename+'.zip', 'w')
  with zip_file:
    for file in filePaths:
      zip_file.write(file)

main("my_dir", "my_dir_archived")

该方法递归地遍历给定文件夹中的每个子文件夹/文件,并将它们写入zip文件,而不是尝试直接压缩文件夹。

好吧,在阅读了这些建议之后,我想出了一个非常类似的方法,它可以在2.7.x中工作,而不会创建“有趣”的目录名(绝对类似的名称),并且只会在zip中创建指定的文件夹。

或者只是为了以防万一,您需要在zip中包含一个包含所选目录内容的文件夹。

def zipDir( path, ziph ) :
 """
 Inserts directory (path) into zipfile instance (ziph)
 """
 for root, dirs, files in os.walk( path ) :
  for file in files :
   ziph.write( os.path.join( root, file ) , os.path.basename( os.path.normpath( path ) ) + "\\" + file )

def makeZip( pathToFolder ) :
 """
 Creates a zip file with the specified folder
 """
 zipf = zipfile.ZipFile( pathToFolder + 'file.zip', 'w', zipfile.ZIP_DEFLATED )
 zipDir( pathToFolder, zipf )
 zipf.close()
 print( "Zip file saved to: " + pathToFolder)

makeZip( "c:\\path\\to\\folder\\to\\insert\\into\\zipfile" )

这是一种现代方法,使用pathlib和上下文管理器。将文件直接放在zip文件中,而不是放在子文件夹中。

def zip_dir(filename: str, dir_to_zip: pathlib.Path):
    with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
        # Use glob instead of iterdir(), to cover all subdirectories.
        for directory in dir_to_zip.glob('**'):
            for file in directory.iterdir():
                if not file.is_file():
                    continue
                # Strip the first component, so we don't create an uneeded subdirectory
                # containing everything.
                zip_path = pathlib.Path(*file.parts[1:])
                # Use a string, since zipfile doesn't support pathlib  directly.
                zipf.write(str(file), str(zip_path))

要将mydirectory的内容添加到新的zip文件中,包括所有文件和子目录:

import os
import zipfile

zf = zipfile.ZipFile("myzipfile.zip", "w")
for dirname, subdirs, files in os.walk("mydirectory"):
    zf.write(dirname)
    for filename in files:
        zf.write(os.path.join(dirname, filename))
zf.close()

现代Python(3.6+)使用pathlib模块对路径进行类似OOP的简洁处理,使用pathlib.Path.rglob()进行递归globing。据我所知,这相当于乔治·V·莱利的回答:压缩压缩,最顶层的元素是目录,保持空目录,使用相对路径。

from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

from os import PathLike
from typing import Union


def zip_dir(zip_name: str, source_dir: Union[str, PathLike]):
    src_path = Path(source_dir).expanduser().resolve(strict=True)
    with ZipFile(zip_name, 'w', ZIP_DEFLATED) as zf:
        for file in src_path.rglob('*'):
            zf.write(file, file.relative_to(src_path.parent))

注意:如可选类型提示所示,zip_name不能是Path对象(将在3.6.2+中修复)。