我正在尝试使用以下代码将名为
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)
基本上有两种使用
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_arc.zip
内创建一个存档 dir1
;唯一的问题是你会得到一个奇怪的行为:在你的档案中你会发现文件dir1_arc.zip
;temp
运行shutil.make_archive(base_name=base_name, format=format, base_dir='dir1')
;你会在 dir1_arc.zip
中得到 temp
,你可以将其解压到 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 的原始回答,否则我会在酱汁中迷失更长时间。
这是 @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"))
您可以使用
Pathlib
和 shutil
:
from pathlib import Path
import shutil
shutil.make_archive(
*dest_path.split('.'),
root_dir=Path(src_path).parent,
base_dir=Path(src_path).name)
)
src_path
是源目录的路径。dest_path
是要创建的目标存档的路径。此解决方案建立在 irudyak 和 Seanbehan 的响应基础上,并使用
Pathlib
。您需要将 source
和 destination
作为 Path 对象传递。
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)
意识到答案是旧的/使用旧的 python 方法。 有新的 python 路径处理方法,但不幸的是,
shutil.make_archive
仍然令人困惑。
请参阅此解决方案以了解现代方式的 python 方法。
def compress_directory_in_winos(
src_dirpath: Path, dst_fpath: Path, fileformat: str = "zip"
):
archive_name = src_dirpath.name
root_dir = src_dirpath.parent.absolute()
base_dir = src_dirpath.absolute().name
output_str = shutil.make_archive(
archive_name,
fileformat,
root_dir=root_dir,
base_dir=base_dir,
)
# it is difficult to control shutil.make_archive how
# and where the output will be;
# but we can easily move it to your dst_fpath
outpath = Path(output_str)
outpath.rename(dst_fpath)