使用 shutil.make_archive() 压缩目录同时保留目录结构
Compressing directory using shutil.make_archive() while preserving directory structure
我正在尝试使用以下代码将名为 test_dicoms
的目录压缩到名为 test_dicoms.zip
的 zip 文件中:
shutil.make_archive('/home/code/test_dicoms', 'zip', '/home/code/test_dicoms')
问题是,当我解压缩它时,/test_dicoms/
中的所有文件都被提取到 /home/code/
而不是文件夹 /test_dicoms/
,并且其中包含的所有文件都被提取提取到 /home/code/
。所以 /test_dicoms/
有一个名为 foo.txt
的文件,在我压缩和解压缩后 foo.txt
的路径是 /home/code/foo.txt
而不是 /home/code/test_dicoms/foo.txt
。我该如何解决?此外,我正在使用的一些目录非常大。我是否需要在我的代码中添加任何内容以使其成为 ZIP64 格式,或者该功能是否足够智能以自动执行此操作?
这是当前创建的存档中的内容:
[gwarner@jazz gwarner]$ unzip -l test_dicoms.zip
Archive: test_dicoms.zip
Length Date Time Name
--------- ---------- ----- ----
93324 09-17-2015 16:05 AAscout_b_000070
93332 09-17-2015 16:05 AAscout_b_000125
93332 09-17-2015 16:05 AAscout_b_000248
使用文档中的术语,您指定了 root_dir,但未指定 base_dir .尝试像这样指定 base_dir:
shutil.make_archive('/home/code/test_dicoms',
'zip',
'/home/code/',
'test_dicoms')
要回答您的第二个问题,这取决于您使用的 Python 版本。从 Python 3.4 开始,ZIP64 扩展将默认可用。在 Python 3.4 之前,make_archive
不会自动创建具有 ZIP64 扩展名的文件。如果您使用的是旧版本的 Python 并且想要 ZIP64,您可以直接调用基础 zipfile.ZipFile()
。
如果你选择直接使用zipfile.ZipFile()
,绕过shutil.make_archive()
,这里有一个例子:
import zipfile
import os
d = '/home/code/test_dicoms'
os.chdir(os.path.dirname(d))
with zipfile.ZipFile(d + '.zip',
"w",
zipfile.ZIP_DEFLATED,
allowZip64=True) as zf:
for root, _, filenames in os.walk(os.path.basename(d)):
for name in filenames:
name = os.path.join(root, name)
name = os.path.normpath(name)
zf.write(name, name)
参考:
我自己写了一个包装函数,因为shutil.make_archive
太难用了
还有代码..
import os, shutil
def make_archive(source, destination):
base = os.path.basename(destination)
name = base.split('.')[0]
format = base.split('.')[1]
archive_from = os.path.dirname(source)
archive_to = os.path.basename(source.strip(os.sep))
shutil.make_archive(name, format, archive_from, archive_to)
shutil.move('%s.%s'%(name,format), destination)
make_archive('/path/to/folder', '/path/to/folder.zip')
我认为,我可以通过删除移动文件来改进 seanbehan 的回答:
def make_archive(source, destination):
base_name = '.'.join(destination.split('.')[:-1])
format = destination.split('.')[-1]
root_dir = os.path.dirname(source)
base_dir = os.path.basename(source.strip(os.sep))
shutil.make_archive(base_name, format, root_dir, base_dir)
基本上有 2 种使用 shutil
的方法:您可以尝试理解其背后的逻辑,也可以只使用一个示例。我在这里找不到示例,所以我尝试创建自己的示例。
;TLDR。 运行 shutil.make_archive('dir1_arc', 'zip', root_dir='dir1')
或 shutil.make_archive('dir1_arc', 'zip', base_dir='dir1')
或 shutil.make_archive('dir1_arc', 'zip', 'dir1')
来自 temp
.
假设你有 ~/temp/dir1
:
temp $ tree dir1
dir1
├── dir11
│ ├── file11
│ ├── file12
│ └── file13
├── dir1_arc.zip
├── file1
├── file2
└── file3
如何创建 dir1
的存档?设置 base_name='dir1_arc'
、format='zip'
。那么你有很多的选择:
cd
变成dir1
和运行shutil.make_archive(base_name=base_name, format=format)
;它将在 dir1
中创建一个存档 dir1_arc.zip
;唯一的问题是你会得到一个奇怪的行为:在你的档案中你会找到文件 dir1_arc.zip
;
- 来自
temp
运行shutil.make_archive(base_name=base_name, format=format, base_dir='dir1')
;您将在 temp
中获得 dir1_arc.zip
,您可以将其解压缩到 dir1
; root_dir
默认为 temp
;
- 来自
~
运行shutil.make_archive(base_name=base_name, format=format, root_dir='temp', base_dir='dir1')
;您将再次获得您的文件,但这次在 ~
目录中;
- 在
~
中创建另一个目录 temp2
并在其中创建 运行:shutil.make_archive(base_name=base_name, format=format, root_dir='../temp', base_dir='dir1')
;您将在这个 temp2
文件夹中获得您的存档;
你可以 运行 shutil
不指定参数吗?你可以。 运行 来自 temp
shutil.make_archive('dir1_arc', 'zip', 'dir1')
。这与 运行 shutil.make_archive('dir1_arc', 'zip', root_dir='dir1')
相同。在这种情况下,我们可以对 base_dir
说些什么?从文档中没有那么多。从源码我们可以看出:
if root_dir is not None:
os.chdir(root_dir)
if base_dir is None:
base_dir = os.curdir
所以在我们的例子中 base_dir
是 dir1
。我们可以继续提问。
我在使用“.”分割某些路径时遇到问题他们中的句点,我发现有一个默认为 'zip' 的可选格式很方便,并且仍然允许您覆盖其他格式并且不易出错。
import os
import shutil
from shutil import make_archive
def make_archive(source, destination, format='zip'):
import os
import shutil
from shutil import make_archive
base, name = os.path.split(destination)
archive_from = os.path.dirname(source)
archive_to = os.path.basename(source.strip(os.sep))
print(f'Source: {source}\nDestination: {destination}\nArchive From: {archive_from}\nArchive To: {archive_to}\n')
shutil.make_archive(name, format, archive_from, archive_to)
shutil.move('%s.%s' % (name, format), destination)
make_archive('/path/to/folder', '/path/to/folder.zip')
特别感谢 seanbehan 的原始回答,否则我会在酱汁中迷路更长时间。
此解决方案基于 irudyak 和 seanbehan 的响应并使用 Pathlib
。您需要将 source
和 destination
作为路径对象传递。
from pathlib import Path
import shutil
def make_archive(source, destination):
base_name = destination.parent / destination.stem
format = (destination.suffix).replace(".", "")
root_dir = source.parent
base_dir = source.name
shutil.make_archive(base_name, format, root_dir, base_dir)
这是@nick 答案的一个变体,它使用 pathlib
、类型提示并避免隐藏内置函数:
from pathlib import Path
import shutil
def make_archive(source: Path, destination: Path) -> None:
base_name = destination.parent / destination.stem
fmt = destination.suffix.replace(".", "")
root_dir = source.parent
base_dir = source.name
shutil.make_archive(str(base_name), fmt, root_dir, base_dir)
用法:
make_archive(Path("/path/to/dir/"), Path("/path/to/output.zip"))
我正在尝试使用以下代码将名为 test_dicoms
的目录压缩到名为 test_dicoms.zip
的 zip 文件中:
shutil.make_archive('/home/code/test_dicoms', 'zip', '/home/code/test_dicoms')
问题是,当我解压缩它时,/test_dicoms/
中的所有文件都被提取到 /home/code/
而不是文件夹 /test_dicoms/
,并且其中包含的所有文件都被提取提取到 /home/code/
。所以 /test_dicoms/
有一个名为 foo.txt
的文件,在我压缩和解压缩后 foo.txt
的路径是 /home/code/foo.txt
而不是 /home/code/test_dicoms/foo.txt
。我该如何解决?此外,我正在使用的一些目录非常大。我是否需要在我的代码中添加任何内容以使其成为 ZIP64 格式,或者该功能是否足够智能以自动执行此操作?
这是当前创建的存档中的内容:
[gwarner@jazz gwarner]$ unzip -l test_dicoms.zip
Archive: test_dicoms.zip
Length Date Time Name
--------- ---------- ----- ----
93324 09-17-2015 16:05 AAscout_b_000070
93332 09-17-2015 16:05 AAscout_b_000125
93332 09-17-2015 16:05 AAscout_b_000248
使用文档中的术语,您指定了 root_dir,但未指定 base_dir .尝试像这样指定 base_dir:
shutil.make_archive('/home/code/test_dicoms',
'zip',
'/home/code/',
'test_dicoms')
要回答您的第二个问题,这取决于您使用的 Python 版本。从 Python 3.4 开始,ZIP64 扩展将默认可用。在 Python 3.4 之前,make_archive
不会自动创建具有 ZIP64 扩展名的文件。如果您使用的是旧版本的 Python 并且想要 ZIP64,您可以直接调用基础 zipfile.ZipFile()
。
如果你选择直接使用zipfile.ZipFile()
,绕过shutil.make_archive()
,这里有一个例子:
import zipfile
import os
d = '/home/code/test_dicoms'
os.chdir(os.path.dirname(d))
with zipfile.ZipFile(d + '.zip',
"w",
zipfile.ZIP_DEFLATED,
allowZip64=True) as zf:
for root, _, filenames in os.walk(os.path.basename(d)):
for name in filenames:
name = os.path.join(root, name)
name = os.path.normpath(name)
zf.write(name, name)
参考:
我自己写了一个包装函数,因为shutil.make_archive
太难用了
还有代码..
import os, shutil
def make_archive(source, destination):
base = os.path.basename(destination)
name = base.split('.')[0]
format = base.split('.')[1]
archive_from = os.path.dirname(source)
archive_to = os.path.basename(source.strip(os.sep))
shutil.make_archive(name, format, archive_from, archive_to)
shutil.move('%s.%s'%(name,format), destination)
make_archive('/path/to/folder', '/path/to/folder.zip')
我认为,我可以通过删除移动文件来改进 seanbehan 的回答:
def make_archive(source, destination):
base_name = '.'.join(destination.split('.')[:-1])
format = destination.split('.')[-1]
root_dir = os.path.dirname(source)
base_dir = os.path.basename(source.strip(os.sep))
shutil.make_archive(base_name, format, root_dir, base_dir)
基本上有 2 种使用 shutil
的方法:您可以尝试理解其背后的逻辑,也可以只使用一个示例。我在这里找不到示例,所以我尝试创建自己的示例。
;TLDR。 运行 shutil.make_archive('dir1_arc', 'zip', root_dir='dir1')
或 shutil.make_archive('dir1_arc', 'zip', base_dir='dir1')
或 shutil.make_archive('dir1_arc', 'zip', 'dir1')
来自 temp
.
假设你有 ~/temp/dir1
:
temp $ tree dir1
dir1
├── dir11
│ ├── file11
│ ├── file12
│ └── file13
├── dir1_arc.zip
├── file1
├── file2
└── file3
如何创建 dir1
的存档?设置 base_name='dir1_arc'
、format='zip'
。那么你有很多的选择:
cd
变成dir1
和运行shutil.make_archive(base_name=base_name, format=format)
;它将在dir1
中创建一个存档dir1_arc.zip
;唯一的问题是你会得到一个奇怪的行为:在你的档案中你会找到文件dir1_arc.zip
;- 来自
temp
运行shutil.make_archive(base_name=base_name, format=format, base_dir='dir1')
;您将在temp
中获得dir1_arc.zip
,您可以将其解压缩到dir1
;root_dir
默认为temp
; - 来自
~
运行shutil.make_archive(base_name=base_name, format=format, root_dir='temp', base_dir='dir1')
;您将再次获得您的文件,但这次在~
目录中; - 在
~
中创建另一个目录temp2
并在其中创建 运行:shutil.make_archive(base_name=base_name, format=format, root_dir='../temp', base_dir='dir1')
;您将在这个temp2
文件夹中获得您的存档;
你可以 运行 shutil
不指定参数吗?你可以。 运行 来自 temp
shutil.make_archive('dir1_arc', 'zip', 'dir1')
。这与 运行 shutil.make_archive('dir1_arc', 'zip', root_dir='dir1')
相同。在这种情况下,我们可以对 base_dir
说些什么?从文档中没有那么多。从源码我们可以看出:
if root_dir is not None:
os.chdir(root_dir)
if base_dir is None:
base_dir = os.curdir
所以在我们的例子中 base_dir
是 dir1
。我们可以继续提问。
我在使用“.”分割某些路径时遇到问题他们中的句点,我发现有一个默认为 'zip' 的可选格式很方便,并且仍然允许您覆盖其他格式并且不易出错。
import os
import shutil
from shutil import make_archive
def make_archive(source, destination, format='zip'):
import os
import shutil
from shutil import make_archive
base, name = os.path.split(destination)
archive_from = os.path.dirname(source)
archive_to = os.path.basename(source.strip(os.sep))
print(f'Source: {source}\nDestination: {destination}\nArchive From: {archive_from}\nArchive To: {archive_to}\n')
shutil.make_archive(name, format, archive_from, archive_to)
shutil.move('%s.%s' % (name, format), destination)
make_archive('/path/to/folder', '/path/to/folder.zip')
特别感谢 seanbehan 的原始回答,否则我会在酱汁中迷路更长时间。
此解决方案基于 irudyak 和 seanbehan 的响应并使用 Pathlib
。您需要将 source
和 destination
作为路径对象传递。
from pathlib import Path
import shutil
def make_archive(source, destination):
base_name = destination.parent / destination.stem
format = (destination.suffix).replace(".", "")
root_dir = source.parent
base_dir = source.name
shutil.make_archive(base_name, format, root_dir, base_dir)
这是@nick 答案的一个变体,它使用 pathlib
、类型提示并避免隐藏内置函数:
from pathlib import Path
import shutil
def make_archive(source: Path, destination: Path) -> None:
base_name = destination.parent / destination.stem
fmt = destination.suffix.replace(".", "")
root_dir = source.parent
base_dir = source.name
shutil.make_archive(str(base_name), fmt, root_dir, base_dir)
用法:
make_archive(Path("/path/to/dir/"), Path("/path/to/output.zip"))