如何将一个巨大的zip文件拆分成多个卷?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将一个巨大的zip文件拆分成多个卷?相关的知识,希望对你有一定的参考价值。

当我通过java.util.zip.*创建一个zip存档时,有没有办法在多个卷中拆分生成的存档?

假设我的整个存档有一个filesize24 MB,我想将它分成3个文件,每个文件的限制为10 MB。 是否有具有此功能的zip API?或者其他任何好方法来实现这一目标?

谢谢索尔斯滕

答案

检查:http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=38&t=004618

我不知道有任何公共API可以帮助您做到这一点。 (虽然如果你不想以编程方式进行,那么像WinSplitter这样的实用程序会这样做)

我没有尝试过,但是,使用ZippedInput / OutputStream时,每个ZipEntry都有一个压缩的大小。在创建压缩文件时,您可能会粗略估计压缩文件的大小。如果您需要2MB的压缩文件,那么在累积的条目大小变为1.9MB后,您可以停止写入文件,对于Manifest文件和其他zip文件特定元素需要.1MB。因此,简而言之,您可以在ZippedInputStream上编写一个包装器,如下所示:

import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ChunkedZippedOutputStream {

    private ZipOutputStream zipOutputStream;

    private String path;
    private String name;

    private long currentSize;
    private int currentChunkIndex;
    private final long MAX_FILE_SIZE = 16000000; // Whatever size you want
    private final String PART_POSTFIX = ".part.";
    private final String FILE_EXTENSION = ".zip";

    public ChunkedZippedOutputStream(String path, String name) throws FileNotFoundException {
        this.path = path;
        this.name = name;
        constructNewStream();
    }

    public void addEntry(ZipEntry entry) throws IOException {
        long entrySize = entry.getCompressedSize();
        if((currentSize + entrySize) > MAX_FILE_SIZE) {
            closeStream();
            constructNewStream();
        } else {
            currentSize += entrySize;
            zipOutputStream.putNextEntry(entry);
        }
    }

    private void closeStream() throws IOException {
        zipOutputStream.close();
    }

    private void constructNewStream() throws FileNotFoundException {
        zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(path, constructCurrentPartName())));
        currentChunkIndex++;
        currentSize = 0;
    }

    private String constructCurrentPartName() {
        // This will give names is the form of <file_name>.part.0.zip, <file_name>.part.1.zip, etc.
        StringBuilder partNameBuilder = new StringBuilder(name);
        partNameBuilder.append(PART_POSTFIX);
        partNameBuilder.append(currentChunkIndex);
        partNameBuilder.append(FILE_EXTENSION);
        return partNameBuilder.toString();
    }
}

上述程序只是一种方法的暗示,而不是任何方式的最终解决方案。

另一答案

如果目标是让输出与pkzip和winzip兼容,我不知道有任何开源库可以做到这一点。我们对其中一个应用程序有类似的要求,最后编写了我们自己的实现(与zip标准兼容)。如果我记得,对我们来说最困难的事情是我们必须动态生成单个文件(大多数zip实用程序的工作方式是创建大型zip文件,然后返回并稍后拆分 - 这样更容易实施。花了大约一天写,2天调试。

zip标准解释了文件格式的外观。如果你不怕卷起袖子,这绝对可行。您必须自己实现一个zip文件生成器,但是您可以使用Java的Deflator类为压缩数据生成段流。你必须自己生成文件和节头,但它们只是字节 - 一旦你潜入就没什么太难了。

这里是zip specification - K部分具有您正在寻找的信息,但您还需要阅读A,B,C和F.如果你正在处理非常大的文件(我们是),你也必须进入Zip64的东西 - 但对于24 MB,你没事​​。

如果你想潜入并尝试 - 如果你遇到问题,回发后我会看看我是否可以提供一些指示。

另一答案

下面的代码是我的解决方案,根据所需的大小将目录结构中的zip文件拆分为块。我发现以前的答案很有用,所以希望用类似但更简洁的方法做出贡献。这段代码适合我的特定需求,我相信还有改进的余地。

private final static long MAX_FILE_SIZE = 1000 * 1000 * 1024; //  around 1GB 
private final static String zipCopyDest =  "C:\\zip2split\\copy";

public static void splitZip(String zipFileName, String zippedPath, String coreId) throws IOException{

    System.out.println("process whole zip file..");
    FileInputStream fis  = new FileInputStream(zippedPath);
    ZipInputStream zipInputStream = new ZipInputStream(fis);
    ZipEntry entry = null;
    int currentChunkIndex = 0;
    //using just to get the uncompressed size of the zipentries
    long entrySize = 0;
    ZipFile zipFile = new ZipFile(zippedPath);
    Enumeration enumeration = zipFile.entries();

    String copDest = zipCopyDest + "\\" + coreId + "_" + currentChunkIndex +".zip";

    FileOutputStream fos = new FileOutputStream(new File(copDest));
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    ZipOutputStream zos = new ZipOutputStream(bos);
    long currentSize = 0; 

    try {
        while ((entry = zipInputStream.getNextEntry()) != null && enumeration.hasMoreElements()) {

            ZipEntry zipEntry = (ZipEntry) enumeration.nextElement();
            System.out.println(zipEntry.getName());
            System.out.println(zipEntry.getSize());
            entrySize = zipEntry.getSize();

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //long entrySize = entry.getCompressedSize();
            //entrySize = entry.getSize(); //gives -1

            if((currentSize + entrySize) > MAX_FILE_SIZE) {
                zos.close();
                //construct a new stream
                //zos = new ZipOutputStream(new FileOutputStream(new File(zippedPath, constructCurrentPartName(coreId))));
                currentChunkIndex++;
                zos = getOutputStream(currentChunkIndex, coreId);
                currentSize = 0;

            }else{
                currentSize += entrySize;
                zos.putNextEntry(new ZipEntry(entry.getName()));
                byte[] buffer = new byte[8192];
                int length = 0;
                while ((length = zipInputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }

                byte[] unzippedFile = outputStream.toByteArray();
                zos.write(unzippedFile);
                unzippedFile = null;
                outputStream.close();
                zos.closeEntry();
            }
            //zos.close();
        }
    } finally {
        zos.close();
    }


}

 public static ZipOutputStream getOutputStream(int i, String coreId) throws IOException {
     System.out.println("inside of getOutputStream()..");
     ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipCopyDest + "\\" + coreId + "_" +  i +".zip"));   
    // out.setLevel(Deflater.DEFAULT_COMPRESSION);
     return out;
 }

public static void main(String args[]) throws IOException{
    String zipFileName = "Large_files _for_testing.zip";
    String zippedPath= "C:\\zip2split\\Large_files _for_testing.zip";
    String coreId = "Large_files _for_testing";
    splitZip(zipFileName, zippedPath, coreId);
}
另一答案

对于它的价值,我喜欢在任何地方使用try-with-resources。如果你进入那种设计模式,那么你会喜欢这个。此外,如果条目大于所需的零件尺寸,这也解决了空零件的问题。在最坏的情况下,您将至少拥有与条目一样多的部分。

在:

没有-archive.zip

日期:

没有-archive.part1of3.zip没有-archive.part2of3.zip没有-archive.part3of3.zip

注意:我正在使用日志记录和Apache Commons FilenameUtils,但您可以随意使用工具包中的内容。

/**
 * Utility class to split a zip archive into parts (not volumes)
 * by attempting to fit as many entries into a single part before
 * creating a new part. If a part would otherwise be empty because
 * the next entry won't fit, it will be added anyway to avoid empty parts.
 *
 * @author Eric Draken, 2019
 */
public class Zip
{
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    private static final String ZIP_PART_FORMAT = "%s.part%dof%d.zip";

    private static final String EXT = "zip";

    private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );

    /**
     * Split a large archive into smaller parts
     *
     * @param zipFile             Source zip file to split (must end with .zip)
     * @param outZipFile          Destination zip file base path. The "part" number will be added automatically
     * @param approxPartSizeBytes Approximate part size
     * @throws IOException Exceptions on file access
     */
    public static void splitZipArchive(
        @NotNull final File zipFile,
        @NotNull final File outZipFile,
        final long approxPartSizeBytes )

以上是关于如何将一个巨大的zip文件拆分成多个卷?的主要内容,如果未能解决你的问题,请参考以下文章

将 zip 存档拆分为多个块

java 如何将多个文件打包成一个zip后进行下载

如何使用 python zipfile 库检查 zip 文件是不是在多个档案中拆分?

将excel根据列名称拆分成多个文件

如何在SSIS中将大型Excel文件拆分为多个小文件?

如何将mysql多个outfiles转换成单个压缩zip