我一直在寻找这个问题,但是找不到我可以使用的任何答复。
是否可以(使用 java )在 zip文件中包含已经缩小的新文件这样,当我解压缩 zip文件后,这些放气的文件会被充气,就像它们被“充气” 到zip工具一样,合并到要压缩的zip中(根据https://docs.oracle.com/javase/8/docs/api/java/util/zip/ZipOutputStream.html#setMethod-int-在ZipEntry中为“ DEFLETED”)。在那种情况下,怎么办?
我从Google和其他搜索者那里获得的有关使用zip文件进行Java压缩的大多数信息可以在以下位置恢复:
我使用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 一样。您知道任何图书馆或其他替代品可以轻松做到吗?我当时想让我自己的ZipOutputStream继承自jdk ZipOutputStream并重写实现此技巧的方法,但是根据这种想法对方法进行“快速复制粘贴和修改”-只是具有“可以工作”的感觉-也没有按照我的希望工作。
之所以要使用此选项,是为了根据需要动态压缩zip文件中的大量文件。 我不确定这是否可以节省时间和CPU,并将压缩后的文件保存在数据库中,并随时选择需要进行压缩的文件。
非常感谢
答案 0 :(得分:1)
zip格式并不是很复杂,因此您应该只提取缩小的数据并在其周围编写自己的zip文件头。该格式记录在here中。如果要从gzip文件导出压缩后的数据,则还应该已经具有CRC和未压缩的长度。 (如果要转换的每个gzip文件都包含一个deflate流,即是一个gzip成员,并且如果确保未压缩的长度小于2 32 个字节,则可以删除“应该”。)
答案 1 :(得分:0)
我可以想象在putNextEntry(e)
和write
之间发生黑客攻击...,您可以使用e.setMethod
。像这样的代码将标头写入putNextEntry
的末尾,并决定在write
中进行压缩,以访问putNextEntry
中给出的条目(不进行复制)。 / p>
在致电closeEntry
之前,您可能需要将其后退。
我没有尝试过,因为您可以尝试得更快。
我不确定这是否可以节省时间并节省将压缩文件保存在数据库中的CPU
我不明白,但我非常怀疑。
OTOH,当给定*.gz
一堆文件时,您的想法似乎也适用,您希望存储它们的纯文本而不进行解压缩和再次压缩。
答案 2 :(得分:0)
最后,我走出了第一步,对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 Directoy,以及在PATH_ZIP_DIR中创建的zip文件。测试类很短,也不太复杂(尽管也像ExtraZipOutputStream类一样是草稿)。我在PATH_IN_DIR中使用了简单的文本文件来测试其压缩率。如您所见,在此类中,deflate()方法是在compress()将文件放入zip文件之前执行deflaton的方法(这要归功于使用STORED的修改过的ExtraZipOutputStream,但就像写入文件相关联的元数据一样它们已由ExtraZipOutputStream本身缩小了。)
在附加到ExtraZipOutputStream.class标题的注释块中,我解释了如何检测对原始代码所做的更改。
答案 3 :(得分:0)
我知道这是一个过时的线程,但是在查看旧文件夹中清理磁盘的过程中,我看到了一个用这个想法完成的“草稿”项目。最后,我们将其丢弃,但如果有人对此感兴趣,我已将其上传到了回购库中。
https://gitlab.com/gylz.mail/dynazip
它甚至不是原型,只是一个简短的示例程序,用于说明其工作方式。正如Mark Alder所说,zip格式并不复杂,在他的回复中指出的链接中对此进行了很好的解释。
答案 4 :(得分:0)
我看了一下ZipOutputStream并想到了这个。
我创建了2个类:PreparedZipEntryBuilder
和PreparedZipEntry
。
这将为给定的字节序列创建最短的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);
}
}