如何将一个巨大的zip文件拆分成多个卷?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将一个巨大的zip文件拆分成多个卷?相关的知识,希望对你有一定的参考价值。
当我通过java.util.zip.*
创建一个zip存档时,有没有办法在多个卷中拆分生成的存档?
假设我的整个存档有一个filesize
的24 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文件拆分成多个卷?的主要内容,如果未能解决你的问题,请参考以下文章