在zip文件中插入已压缩的文件



我一直在搜索这个问题,但找不到任何可以使用的响应。

是否可以(使用java)将已经压缩的新文件包括在zip文件压缩压缩文件时,这些收缩的文件被膨胀,就像它们被"膨胀">传递到zip工具并被合并到要压缩的zip中一样(ZipEntry中的"DEFLETED"根据https://docs.oracle.com/javase/8/docs/api/java/util/zip/ZipOutputStream.html#setMethod-int-)。那样的话,怎么办?

我从谷歌和其他搜索者那里得到的关于压缩压缩zip文件的大部分信息可以在中恢复

  • 将zip作为文件系统打开https://fahdshariff.blogspot.com/2011/08/java-7-working-with-zip-files.html
  • 直接使用流(https://www.java-examples.com/create-zip-file-using-zipoutputstream-example)

我使用JZlib偏转文件(但您可以使用任何其他库。例如,http://www.avajava.com/tutorials/lessons/how-do-i-deflate-and-inflate-a-file.html)

正如你对我所说的那样,当我试图插入已经放气的文件时,它们会再次放气(使用ZipEntry中的DEFLETED方法,这是默认的),当zip文件被解压缩时,文件会被放气到以前已经放气的状态。

查看oracle中ZipOutputStream.java的源代码,您可以看到有两种方法可以将条目添加到zip中:

DEFLATED (an integer set to 20)
STORED   (an integer set to 10)

我想要的是将收缩的条目添加到zip中作为STORED,但一旦添加它们,就可以更改自己zip中的信息,就像它们被处理为deflated一样。你知道有什么图书馆或其他可以轻松做到这一点的地方吗?我想从jdk ZipOutputStream中继承并重写方法来制作我自己的ZipOutputStream,但根据这个想法对方法进行"快速复制粘贴和修改"——只是为了有一种"它可以工作"的感觉——也没有按照我的希望工作。

我之所以希望有这个选项,是为了根据需要动态压缩zip文件中的大量文件我不确定这是否可以节省时间和cpu,将压缩后的文件保存在数据库中,并随时选择那些被请求进行zip的文件。

非常感谢

zip格式并不是很复杂,所以你应该只使用压缩后的数据,并围绕它编写自己的zip文件头。如果您是从gzip文件中导出压缩后的数据,那么您也应该已经有了CRC和未压缩的长度。(如果你想转换的每个gzip文件都由一个deflate流组成,即一个gzip成员,并且如果未压缩的长度被保证小于232字节,那么你可以删除该语句中的"应该"。)

我可以想象一个黑客。。。。在putNextEntry(e)write之间,可以使用e.setMethod。类似它的代码在putNextEntry的末尾写入头,并决定在write中进行压缩,在那里它访问putNextEntry中给出的条目(不进行复制)。

在调用closeEntry之前,您可能需要将其向后翻转。

我没有试过,因为你可以试得更快。


我不确定这是否可以节省时间和cpu,将压缩后的文件保存在数据库中

我不明白,但我很怀疑。

OTOH当给定一堆*.gz文件时,您的想法看起来也适用,您希望存储它们的明文,而无需再次解压缩和压缩。

最后,我从第一种方法走得更远,更深入地分析了jdk源代码,并花了一些时间调试和修改它:

https://gist.github.com/gylz/b2db94ce55f1829f2e2a2cd498092d46

https://gist.github.com/gylz/284d8b891fc0bbd3161d1ec5929be074

如果你想尝试它,你必须在Test类的变量PATH_ZIP_DIR、PATH_in_DIR、PATH_TMP_DIR中指示你想要的路径。要压缩的文件取自PATH_IN_DIR目录和在PATH_zip_DIR中创建的zip文件。测试类很短,也不太复杂(尽管它也是ExtraZipOutputStream类的草案)。我在PATH_in_DIR中使用了简单的文本文件来测试它们的压缩。正如您所看到的,在这个类中,deflate()方法是在compress()将文件放入zip文件之前执行爆燃的方法(由于修改了ExtraZipOutputStream,使用STORED,但写入与文件相关的元数据,就好像它们已经被ExtraZipOututStream本身放气一样。)

在附加到ExtraZipOutputStream.class标题的注释块中,我解释了如何检测我对原始代码所做的更改。

我知道这是一个过时的线程,但在清理磁盘的旧文件夹中,我看到了一个"草稿";我用这个想法为我的工作做的项目。最后,我们丢弃了它,但如果有人对它感兴趣的话,我已经把它上传到了回购…

https://gitlab.com/gylz.mail/dynazip

它甚至不是一个原型,只是一个简短的示例程序,用来展示它是如何工作的。正如Mark Alder所说,zip格式并不复杂,他在回应中指出的链接对此进行了很好的解释。

我看了一眼ZipOutputStream&想出了这个。

我创建了两个类:PreparedZipEntryBuilder&CCD_ 10。

这将为给定的字节序列创建最短的ZipEntry。

在PreparedZipEntryBuilder中,我包含了许多测试用例。

它是完全无创的,非常简单&评论贯穿始终。

玩得开心。

这是PreparedZipEntry:

package com.stackoverflow.preparedzipentry;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class PreparedZipEntry {
private final int    originalLength;
private final long   originalCRC;
private final int    deflatedLength;
private final int    bestMethodDeterminedByBuilder;
private final byte[] preparedEntryBytes;
private final long   preparedEntryBytesCRC;

/* package private */ PreparedZipEntry(final int originalLength, final long originalCRC, final int deflatedLength, final int method, final byte[] preparedEntryBytes) {
final CRC32 preparedEntryBytesCRC32 = new CRC32();
/**/        preparedEntryBytesCRC32.update(preparedEntryBytes);
/*
* These 6 Fields are all you need to correctly insert a prepared entry.
* If desired, they can be written to a File for later use.
* (in that case you might want to pass the original bytes to this constructor too?)
*/
this.originalLength                = originalLength;
this.originalCRC                   = originalCRC;
this.deflatedLength                = deflatedLength;
this.bestMethodDeterminedByBuilder = method;
this.preparedEntryBytes            = preparedEntryBytes;
this.preparedEntryBytesCRC         = preparedEntryBytesCRC32.getValue();
}
/**
* Writes our PreparedZipEntry to the Outputstream.
* <p>
* You may set the FileTimes in the returned ZipEntry.<br>
* LastModifiedTime will be used to create the Zip-Directory @ EOF
* 
* @param   zos
* @param   entryName
* @return
* @throws  IOException
*/
public ZipEntry writeEntry(final ZipOutputStream zos, final String entryName) throws IOException {
final ZipEntry entry = new ZipEntry(entryName);
/*
* Set the Sizes correctly for the Entry Header & write an Entry for the desired Method...
*/
entry.setSize          (this.originalLength);
entry.setCompressedSize(this.deflatedLength);
entry.setCrc           (this.originalCRC);
entry.setMethod        (this.bestMethodDeterminedByBuilder);  // Must use this Method (influences LOC-Header construction)
zos.putNextEntry(entry);
/*
* Now set the byte-count to what write(...) is expecting for the prepared bytes and write them as STORED...
*/
entry.setMethod(ZipEntry.STORED);
entry.setSize  (this.preparedEntryBytes.length);
zos.write      (this.preparedEntryBytes);
/*
* Now set the CRC to what closeEntry() is expecting for the bytes just STORED & close the Entry...
*/
entry.setCrc   (this.preparedEntryBytesCRC);
zos.closeEntry();
/*
* Finally, set the Sizes, CRC & Method correctly once more...
* (ZipOutputStream will use these later to write the Zip-Directory @ EOF)
*/
entry.setSize  (this.originalLength);
entry.setCrc   (this.originalCRC);
entry.setMethod(this.bestMethodDeterminedByBuilder);
return entry;
}
}

这里是建设者:

package com.stackoverflow.preparedzipentry;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class PreparedZipEntryBuilder {
public static PreparedZipEntry of(final byte[] entryBytes) {
final int    entryBytesLength    = entryBytes.length;
final long   entryBytesCRC       = getCRC32(entryBytes);
final byte[] deflatedBytes       = getDeflatedBytes(entryBytes);
final int    deflatedBytesLength = deflatedBytes.length;
/*
* Depending on how well the bytes compress, generate an uncompressed or compressed PreparedZipEntry...
*/
if (deflatedBytesLength < entryBytesLength) {
/*
* Compressed length was less than the uncompressed length
*/
return new PreparedZipEntry(entryBytesLength, entryBytesCRC, deflatedBytesLength, ZipEntry.DEFLATED, deflatedBytes);
} else {
return new PreparedZipEntry(entryBytesLength, entryBytesCRC, entryBytesLength,    ZipEntry.STORED,   entryBytes);
/*
* Uncompressed was shorter!
*/
}
}
private static byte[] getDeflatedBytes(final byte[] bytes) {
try(final ByteArrayOutputStream baos = new ByteArrayOutputStream((int) (bytes.length * 0.4 /* Guess: 40% */));
final          OutputStream bos  = new  BufferedOutputStream(baos);
final  DeflaterOutputStream dos  = new  DeflaterOutputStream(bos, new Deflater(Deflater.BEST_COMPRESSION, true)))
{
dos.write(bytes, 0, bytes.length);
dos.close();
return baos.toByteArray();
}
catch (final           IOException cannotHappen) {
throw new UncheckedIOException(cannotHappen);
}
}
private static long getCRC32(final byte[] bytes) {
final CRC32 crc32 = new CRC32();
/**/        crc32.update(bytes);
return      crc32.getValue();
}
private static ZipEntry writeRegularEntryUnknown(final ZipOutputStream zos, final String entryName, final byte[] entryBytes) throws IOException {
final byte[] deflatedBytes       = getDeflatedBytes(entryBytes);
final ZipEntry entry = new ZipEntry(entryName);
/**/           entry.setLastModifiedTime(FileTime.from(Instant.now())); // For test: not absolutely necessary.
entry.setSize          (entryBytes   .length);
entry.setCompressedSize(deflatedBytes.length);
entry.setCrc           (getCRC32(entryBytes));
entry.setMethod        (ZipEntry.DEFLATED);
zos.putNextEntry(entry);
zos.write       (entryBytes, 0, entryBytes.length);
zos.closeEntry();
return entry;
}
private static ZipEntry writeRegularEntryKnown(final ZipOutputStream zos, final String entryName, final byte[] entryBytes) throws IOException {
final ZipEntry entry = new ZipEntry(entryName);
/**/           entry.setLastModifiedTime(FileTime.from(Instant.now())); // For test: not absolutely necessary.
/**/           entry.setMethod        (ZipEntry.DEFLATED);
zos.putNextEntry(entry);
zos.write       (entryBytes, 0, entryBytes.length);
zos.closeEntry();
return entry;
}
public static void main(String[] args) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ZipOutputStream        zos = new ZipOutputStream(baos);
/**/                         zos.setLevel(Deflater.BEST_COMPRESSION);
final String string0s = ">00000000<";
final String string09 = "0123456789";
final String stringaz = "abcdefghijklmnopqrstuvwxyz";
final String stringAZ = stringaz.toUpperCase();
int z = 1000;
for (final String entryText : new String[] {
"Short",
"Compresses poorly : "    + string09 + stringaz + stringAZ,
"Compresses well : "      + string09 + string09 + string09 + string09 + string09 + string09,
"Compresses very well : " + string0s + string0s + string0s + string0s + string0s + string0s,
"-----------------------------------------------------------------------------------------", // <- Compresses extremely well
}) {
for (final String ab : new String[] {"a", "b"}) {
final PreparedZipEntry prepared =  PreparedZipEntryBuilder.of(entryText.getBytes());
/**/                   prepared.writeEntry     (zos, z++ + ab + "_Prepared");
/**/                   writeRegularEntryKnown  (zos, z++ + ab + "_Regular_KnownLength",   entryText.getBytes());
final ZipEntry  last = writeRegularEntryUnknown(zos, z++ + ab + "_Regular_unknownLength", entryText.getBytes());
/**/            last.setLastModifiedTime(FileTime.from(Instant.now().minusSeconds(99 * z)));
}
}
zos.close();
final byte[] mainBytes = baos.toByteArray();
final Path path = Paths.get("MultiMemberPredeflated.zip");
Files.deleteIfExists(path);
Files.write         (path, mainBytes, StandardOpenOption.CREATE);
}
}

最新更新