【问题标题】:Split a zip archive into multiple chunks将 zip 存档拆分为多个块
【发布时间】:2018-09-05 21:34:15
【问题描述】:

我正在尝试创建一个可能很大的文件夹的 zip 存档。 为此,我使用了 python zipfile 模块,但据我所知,没有选项可以将创建的存档拆分为具有最大大小的多个块。

压缩存档应该通过 Telegram 发送,每个文件的大小限制为 1.5 GB。因此,我需要拆分生成的 zip 存档。

我真的不想使用子进程和 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

提前致谢

【问题讨论】:

    标签: python-3.x zipfile


    【解决方案1】:

    这是我用来通过电报机器人将文件发送到电报频道的内容。 电报机器人上传的文件大小限制为 50MB 电报客户端上传的文件大小限制为 1500MB,但您可以添加一些文本或其他信息,以便 1495 更安全

    #! /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 在我使用子进程调用 7zip 来执行此操作之前,我检查了其他压缩工具,但没有人可以拆分文件,而某些 7z 端口的 python lib 无法执行此操作。我也尝试在linux下使用split cmd,但是windows用户很难结合起来。
    • @Nukesor 我发现有人使用 HJSplit 来拆分文件,我在 github 中找到了一个项目,将其作为 lib github.com/marcomg/openhjsplit
    【解决方案2】:

    如果您没有找到更好的本地方式使用 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
    

    类似于将其重新组合到接收器一侧。

    【讨论】:

    • 一般来说是个好主意,但是这个解决方案的问题是最终用户应该只是从电报中下载并提取它,而不需要任何额外的程序。或者这是实现 zip 拆分的常用方法,只需剪切 zip 文件并正确索引它。
    猜你喜欢
    • 2023-03-20
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 2023-03-03
    • 2017-05-05
    相关资源
    最近更新 更多