将 zip 存档拆分为多个块

Posted

技术标签:

【中文标题】将 zip 存档拆分为多个块【英文标题】:Split a zip archive into multiple chunks 【发布时间】: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

提前致谢

【问题讨论】:

【参考方案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", "-vm".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 文件并正确索引它。

以上是关于将 zip 存档拆分为多个块的主要内容,如果未能解决你的问题,请参考以下文章

如何在java中找到拆分的存档?

使用 webpack 将供应商库拆分为多个块

Pandas 和多处理内存管理:将 DataFrame 拆分为多个块

D:如何将文件添加到 zip 存档中?

如何将子字符串拆分为多个块并使用 C# 将它们输入到表中?

将多个空格从文本文件拆分为数组