将多个Python目录压缩成一个zip文件

5
我有一个名为ds237的顶层目录,其下有多个子目录如下所示:
ds237/
├── dataset_description.json
├── derivatives
├── sub-01
├── sub-02
├── sub-03
├── sub-04
├── sub-05
├── sub-06
├── sub-07
├── sub-08
├── sub-09
├── sub-10
├── sub-11
├── sub-12
├── sub-13
├── sub-21
├── sub-22
├── sub-23
├── sub-24
├── sub-25
├── sub-26
├── sub-27
├── sub-28
├── sub-29

我尝试根据 zip 文件的大小从 ds237 中创建多个带有适当 zip 名称的 zip 文件。

sub01-01.zip: 包含 sub-01 到 sub-07 sub08-13.zip : 包含 sub08 到 sub-13

我编写了一个逻辑,该逻辑创建子目录列表 [sub-01,sub-02,sub-03,sub-04,sub-05]。 我创建了这个列表以便列表中所有子目录的总大小不应超过 5GB。

我的问题:如何编写函数将这些子目录(在列表中)压缩到具有适当名称的目标 zip 文件中。 基本上,我想编写以下函数:

def zipit([子目录列表], '路径/到/zipfile/sub*-*.zip'):

在 Linux 中,我通常通过以下方式实现此目标:

'zip -r compress/sub01-08.zip ds237/sub-0[1-8]'
4个回答

14

看一下https://dev59.com/-3I-5IYBdhLWcg3wbXxN#1855118,你可以重复使用那个答案中的函数将一个目录添加到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, '..')))


def zipit(dir_list, zip_name):
    zipf = zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED)
    for dir in dir_list:
        zipdir(dir, zipf)
    zipf.close()

应该使用已经分块的列表和给定的名称来调用zipit函数。如果您想要使用程序化的名称,可以使用字符串格式化(例如:"path/to/zipfile/sub{}-{}.zip".format(start, end))。


以上脚本将通过排除目录路径来创建一个zip文件。 假设我将/Users/aba/ds100/sub-0[1-6]压缩成sub01-06.zip,那么当我解压缩该zip文件时,它应该生成以下路径ds100/sub-01和其他目录。 - learnningprogramming
1
你也可以更改 relpath,使其从 path 向上移动两个目录。因此,将 os.path.join(path, '..') 更改为 os.path.join(path, '..', '..'),它应该可以工作。 - Jerr
它部分地完成了工作,但是当我解压缩sub01-06.zipsub07-09.zip时,理想情况下应该解压缩到ds100/sub-01 ds100/sub-02 ds100/sub-03 ds100/sub-04 ds100/sub-05 ds100/sub-06 ds100/sub-07 ds100/sub-08 ds100/sub-09,然而,根据您建议的更改,上述脚本创建了两个不同的ds100 - learnningprogramming
不确定你看到了什么,我进行了类似的测试,并能够提取两个zip文件来填充ds100目录。可能是您的解压工具有一些配置问题。您还可以使用unzip zip_file.zip -d output_directory将文件zip_file.zip解压缩到output_directory。这也是将代码更改为放置ds100的替代方法,您只需将输出目录指定为ds100即可。 - Jerr

1
下面将给您一个带有第一个文件夹ds100的zip文件:
import os
import zipfile    

def zipit(folders, zip_filename):
    zip_file = zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED)

    for folder in folders:
        for dirpath, dirnames, filenames in os.walk(folder):
            for filename in filenames:
                zip_file.write(
                    os.path.join(dirpath, filename),
                    os.path.relpath(os.path.join(dirpath, filename), os.path.join(folders[0], '../..')))

    zip_file.close()


folders = [
    "/Users/aba/ds100/sub-01",
    "/Users/aba/ds100/sub-02",
    "/Users/aba/ds100/sub-03",
    "/Users/aba/ds100/sub-04",
    "/Users/aba/ds100/sub-05"]

zipit(folders, "/Users/aba/ds100/sub01-05.zip")

例如,sub01-05.zip 的结构类似于:
ds100
├── sub-01
|   ├── 1
|       ├── 2
|   ├── 1
|   ├── 2
├── sub-02
    ├── 1
        ├── 2
    ├── 1
    ├── 2

1
你可以使用subprocess调用'zip'并将路径作为参数传递。

我打算用Pythonic的方式来实现这个。 - learnningprogramming

0
批量压缩文件,改进自之前的答案,您可以使用以下方法。
import os
from zipfile import ZipFile, ZIP_DEFLATED

base_dir = "."
base_zip_dir = f"{base_dir}/zip"
target_dir = f"{base_dir}/data"
folders_per_zip = 500

os.makedirs(base_zip_dir, exist_ok=True)

def zipdir(path, ziph):
    for root, _, 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, '..')))

def batch_zip(folder_list, folders_per_zip, target_dir):
    for i, folders in enumerate(zip(*[iter(folder_list)] * folders_per_zip), start=1):
        zip_filename = f"{target_dir}/{i}.zip"
        with ZipFile(zip_filename, 'w', ZIP_DEFLATED) as zipf:
            for folder in folders:
                if any(os.path.isfile(os.path.join(folder, f)) for f in os.listdir(folder)):
                    zipdir(folder, zipf)
        print(f"Zip file {zip_filename} created.")

folders = [f.path for f in os.scandir(target_dir) if f.is_dir()]
batch_zip(folders , folders_per_zip, base_zip_dir)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接