[英]How do I create a zip file of a file path using Python, including empty directories?
我一直在尝试使用zipfile
和shutil.make_archive
模块递归创建目录的 zip 文件。 两个模块都工作得很好——除了空目录不会被添加到档案中。 包含其他空目录的空目录也会被静默跳过。
我可以使用 7Zip 创建相同路径的存档并保留空目录。 因此我知道这在文件格式本身中是可能的。 我只是不知道如何在 Python 中做到这一点。 有任何想法吗? 谢谢!
有一个使用 zipfile 的示例:
import os, zipfile
from os.path import join
def zipfolder(foldername, filename, includeEmptyDIr=True):
empty_dirs = []
zip = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(foldername):
empty_dirs.extend([dir for dir in dirs if os.listdir(join(root, dir)) == []])
for name in files:
zip.write(join(root ,name))
if includeEmptyDIr:
for dir in empty_dirs:
zif = zipfile.ZipInfo(join(root, dir) + "/")
zip.writestr(zif, "")
empty_dirs = []
zip.close()
if __name__ == "__main__":
zipfolder('test1/noname/', 'zip.zip')
这是从使用 python 将文件夹添加到 zip 文件中提升的,但这是我尝试过的唯一有效的功能。 作为答案列出的那个在 Python 2.7.3 下不起作用(不复制空目录并且效率低下)。 以下经过尝试和测试:
#!/usr/bin/python
import os
import zipfile
def zipdir(dirPath=None, zipFilePath=None, includeDirInZip=True):
if not zipFilePath:
zipFilePath = dirPath + ".zip"
if not os.path.isdir(dirPath):
raise OSError("dirPath argument must point to a directory. "
"'%s' does not." % dirPath)
parentDir, dirToZip = os.path.split(dirPath)
#Little nested function to prepare the proper archive path
def trimPath(path):
archivePath = path.replace(parentDir, "", 1)
if parentDir:
archivePath = archivePath.replace(os.path.sep, "", 1)
if not includeDirInZip:
archivePath = archivePath.replace(dirToZip + os.path.sep, "", 1)
return os.path.normcase(archivePath)
outFile = zipfile.ZipFile(zipFilePath, "w",
compression=zipfile.ZIP_DEFLATED)
for (archiveDirPath, dirNames, fileNames) in os.walk(dirPath):
for fileName in fileNames:
filePath = os.path.join(archiveDirPath, fileName)
outFile.write(filePath, trimPath(filePath))
#Make sure we get empty directories as well
if not fileNames and not dirNames:
zipInfo = zipfile.ZipInfo(trimPath(archiveDirPath) + "/")
#some web sites suggest doing
#zipInfo.external_attr = 16
#or
#zipInfo.external_attr = 48
#Here to allow for inserting an empty directory. Still TBD/TODO.
outFile.writestr(zipInfo, "")
outFile.close()
您需要注册一种新的存档格式才能做到这一点,因为默认的 ZIP 存档器不支持这种格式。 查看现有 ZIP 归档程序的主要内容。 使用当前未使用的dirpath
变量创建您自己的归档程序。 我寻找如何创建一个空目录并找到了这个:
zip.writestr(zipfile.ZipInfo('empty/'), '')
有了这个,您应该能够编写必要的代码以使其归档空目录。
(我仍然没有声誉,不能直接发表评论)
建议编辑詹姆斯的答案,
将'def zipdir(dirPath = None,zipFilePath = None,....')更改为:'def zipdir(dirPath,zipFilePath = None,...')或删除'if not os.path.isdir(dirPath) :'条件和身体
否则,由于这种不一致,您将遇到问题。
def zip_dir(src_dir, dst_zip, *, skip_suffixes=None, dry=False):
import logging
from pathlib import Path
from os import walk
from tempfile import TemporaryDirectory
from zipfile import ZipFile, ZipInfo
_log = logging.getLogger(zip_dir.__name__)
_log.addHandler(logging.NullHandler())
_sep = 50 * "-"
skip_suffixes = skip_suffixes or []
src_dir, dst_zip = Path(src_dir), Path(dst_zip)
_log.info("zipping dir: '%s' to: '%s", str(src_dir), str(dst_zip))
if not src_dir.exists():
raise FileNotFoundError(str(src_dir))
if not src_dir.is_dir():
raise NotADirectoryError(str(src_dir))
if dst_zip.exists():
raise FileExistsError(str(dst_zip))
with TemporaryDirectory() as tmp_dir:
tmp_zip_path = Path(tmp_dir).joinpath(dst_zip.name)
with ZipFile(str(tmp_zip_path), mode="w") as zip_out:
for root, dirs, files in walk(src_dir):
root = Path(root)
for folder in dirs:
folder = root.joinpath(folder)
# add empty folders to the zip
if not list(folder.iterdir()):
_log.debug(_sep)
folder_name = f"{str(folder.relative_to(src_dir))}/"
_log.debug("empty dir: '%s'", folder_name)
if dry:
continue
zip_out.writestr(ZipInfo(folder_name), "")
for file in files:
file = root.joinpath(file)
_log.debug(_sep)
_log.debug("adding: '%s'", str(file))
should_skip = None
for suffix in file.suffixes:
if suffix in skip_suffixes:
should_skip = suffix
break
if should_skip:
_log.debug("skipped [%s]: %s", should_skip, str(file))
continue
arcname = str(file.relative_to(src_dir))
_log.debug("arcname: '%s'", arcname)
if dry:
continue
zip_out.write(str(file), arcname=arcname)
if not dry:
dst_zip.write_bytes(tmp_zip_path.read_bytes())
tmp_zip_path.unlink()
if __name__ == '__main__':
import logging
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s | %(levelname)8s | %(module)25s:%(lineno)-5s | %(message)s")
zip_dir("/tmp/opera_profile", "opera_profile.zip", skip_suffixes=[".log"], dry=True)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.