将一个zip压缩包拆分成多个部分

4
我正在尝试创建一个可能非常庞大的文件夹的zip归档文件。 为此,我使用 Python 的 zipfile 模块,但据我所见,没有选项将创建的归档文件拆分成多个大小不超过某个值的块。
压缩后的归档文件应通过 Telegram 发送,其大小限制为每个文件 1.5 GB。因此,我需要拆分生成的 zip 归档文件。
我真的很不想使用 subprocess 和 shell 命令来创建这个归档文件。
我的当前代码如下:
def create_zip(archive_name, directory):
    """Create a zip file from given dir path."""
    with ZipFile(archive_name, "w", ZIP_LZMA) as target_zip_file:
        for root, _, files in os.walk(directory):
            for file_to_zip in files:
                absolute_path = os.path.join(root, file_to_zip)
                zip_file_name = absolute_path[len(directory) + len(os.sep):]
                target_zip_file.write(absolute_path, zip_file_name)

    return target_zip_file

提前致谢

2个回答

2

以下是我用来通过Telegram机器人将文件发送到电报频道的方法。

通过Telegram机器人上传的文件大小限制为50MB。

通过Telegram客户端上传的文件大小限制为1500MB,但您可以添加一些文本或其他信息,这样1495MB更加安全。

"Original Answer"翻译成中文为"最初的回答"。

#! /usr/bin/python3
# -*- coding:utf-8 -*-
# apt-get install p7zip-full

import subprocess
import os
import math
import logzero

logger = logzero.logger

MAX_SPLIT_SIZE = 1495

    def file_split_7z(file_path, split_size=MAX_SPLIT_SIZE):
        file_path_7z_list = []
        # if origin file is 7z file rename it
        origin_file_path = ""
        if os.path.splitext(file_path)[1] == ".7z":
            origin_file_path = file_path
            file_path = os.path.splitext(origin_file_path)[0] + ".7zo"
            os.rename(origin_file_path, file_path)
        # do 7z compress
        fz = os.path.getsize(file_path) / 1024 / 1024
        pa = math.ceil(fz / split_size)
        head, ext = os.path.splitext(os.path.abspath(file_path))
        archive_head = "".join((head, ext.replace(".", "_"))) + ".7z"
        for i in range(pa):
            check_file_name = "{}.{:03d}".format(archive_head, i + 1)
            if os.path.isfile(check_file_name):
                logger.debug("remove exists file | {}".format(check_file_name))
                os.remove(check_file_name)
        cmd_7z = ["7z", "a", "-v{}m".format(split_size), "-y", "-mx0", archive_head, file_path]
        proc = subprocess.Popen(cmd_7z, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = proc.communicate()
        if b"Everything is Ok" not in out:
            logger.error("7z output | {}".format(out.decode("utf-8")))
            logger.error("7z error | {}".format(err.decode("utf-8")))
            return file_path_7z_list

        for i in range(pa):
            file_path_7z_list.append("{}.{:03d}".format(archive_head, i + 1))
        # if origin file is 7z file rename it back
        if origin_file_path:
            os.rename(file_path, origin_file_path)
        return file_path_7z_list

    def do_file_split(file_path, split_size=MAX_SPLIT_SIZE):
        """caculate split size 
           example max split size is 1495 file size is 2000
           than the split part num should be int(2000 / 1495 + 0.5) = 2
           so the split size should be 1000 + 1000 but not 1495 + 505
           with the file size increase the upload risk would be increase too
        """
        file_size = os.path.getsize(file_path) / 2 ** 20
        split_part = math.ceil(file_size / split_size)
        new_split_size = math.ceil(file_size / split_part)
        logger.info("file size | {} | split num | {} | split size | {}".format(file_size, split_part, new_split_size))
        file_path_7z_list = file_split_7z(file_path, split_size=new_split_size)
        return file_path_7z_list

我现在也正在使用类似的方法,但我很想看到一些Python本地或至少是一些库,可以在不使用子进程的情况下压缩文件... - Nukesor
在使用subprocess调用7zip之前,我检查了其他压缩工具,但没有一个能够分割文件,而一些移植7z的Python库也无法做到这一点。我还尝试在Linux下使用spilt命令,但对于Windows用户来说合并它很困难。 - Yuanmeng Xiao
@Nukesor,我发现有人在使用HJSplit来分割文件,并且我在Github上找到了一个将其作为库封装的项目https://github.com/marcomg/openhjsplit - Yuanmeng Xiao

1

如果您在使用zipfile时找不到更好、本地的方式,仍然可以自己编写文件分割算法。例如:

outfile = archive_name
packet_size = int(1.5 * 1024**3)   # bytes

with open(outfile, "rb") as output:
    filecount = 0
    while True:
        data = output.read(packet_size)
        print(len(data))
        if not data:
            break   # we're done
        with open("{}{:03}".format(outfile, filecount), "wb") as packet:
            packet.write(data)
        filecount += 1

类似地,在接收方重新组合它。


1
一般来说这是个好主意,但是这个解决方案的问题在于,最终用户只能从 Telegram 下载并提取它,而不需要任何其他程序。或者这是常见的 zip 分割方式,只需切割zip文件并正确地索引即可实现。 - Nukesor

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