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


当前回答

显而易见的方法是使用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文件,而不是尝试直接压缩文件夹。

其他回答

正如其他人所指出的,您应该使用zipfile。文档告诉哪些函数可用,但并没有真正解释如何使用它们压缩整个目录。我认为用一些示例代码来解释是最简单的:

import os
import zipfile
    
def zipdir(path, ziph):
    # ziph is zipfile handle
    for root, dirs, files in os.walk(path):
        for file in files:
            ziph.write(os.path.join(root, file), 
                       os.path.relpath(os.path.join(root, file), 
                                       os.path.join(path, '..')))

with zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED) as zipf:
    zipdir('tmp/', zipf)

要将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()

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

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。

好吧,在阅读了这些建议之后,我想出了一个非常类似的方法,它可以在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" )

此函数将递归地压缩目录树,压缩文件,并在存档中记录正确的相对文件名。存档条目与zip-r output.zip source_dir生成的条目相同。

import os
import zipfile
def make_zipfile(output_filename, source_dir):
    relroot = os.path.abspath(os.path.join(source_dir, os.pardir))
    with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip:
        for root, dirs, files in os.walk(source_dir):
            # add directory (needed for empty dirs)
            zip.write(root, os.path.relpath(root, relroot))
            for file in files:
                filename = os.path.join(root, file)
                if os.path.isfile(filename): # regular files only
                    arcname = os.path.join(os.path.relpath(root, relroot), file)
                    zip.write(filename, arcname)