Я искал эту проблему, но не нашел ответа, который можно было бы использовать.
Возможно ли (с использованием Ява) включить новые файлы, которые являются уже спущен, в zip файл таким образом, чтобы после того, как я декомпрессировать zip-файл, эти сжатые файлы были надуты так же, как если бы их передали «надутые» для инструмента zip и были включены в zip для сжатия (" ОТКАЗАНО "в ZipEntry согласно https://docs.oracle.com/javase/8/docs/api/java/util/zip/ZipOutputStream.html#setMethod-int-). В таком случае как?
Большая часть информации о сжатии с помощью java для zip-файлов, которую я получаю от Google и других поисковиков, может быть возобновлена в:
Я отклоняю файлы с помощью JZlib (но вы можете делать что угодно и с любой другой библиотекой. Пример, http://www.avajava.com/tutorials/lessons/how-do-i-deflate-and-inflate-a-file.html)
Как и следовало ожидать из того, что я сказал, когда я пытаюсь вставить уже спущенные файлы, они снова сдуваются (с использованием метода DEFLETED в ZipEntry, который используется по умолчанию), а когда zip-файл распаковывается, файлы сдуваются до в их бывшем уже спущенном состоянии.
Посмотрев на источник ZipOutputStream.java из oracle, вы увидите, что есть два метода добавления записей в zip-архив:
DEFLATED (an integer set to 20)
STORED (an integer set to 10)
Я хочу добавить дефлированные записи как СОХРАНЕННЫЕ в zip-архиве, но как только они будут добавлены, чтобы изменить информацию в собственном zip-архиве, как если бы они были обработаны DEFLATED. Знаете ли вы какую-нибудь библиотеку или альтернативу, чтобы сделать это легко? Я думал создать свой собственный ZipOutputStream, унаследованный от jdk ZipOutputStream и переопределив методы, чтобы сделать трюк, но «быстрое копирование-вставка и модификация» методов в соответствии с этой идеей - просто чтобы иметь ощущение «это может сработать» - тоже не оправдала моих надежд.
Причина, по которой я хотел бы иметь эту опцию, заключается в динамическом сжатии огромного количества файлов в zip-файле по запросу. Я не уверен, может ли это сэкономить время и ЦП, сохраняя спущенные файлы в базе данных. и каждый момент выбирая те файлы, которые запрашиваются для создания zip-архивов.
Большое спасибо,




Я мог представить себе взлом ... между putNextEntry(e) и write, вы могли бы использовать e.setMethod. Такой код, как будто он записывает заголовок в конце putNextEntry и решает о сжатии в write, где он обращается к той самой записи, которая была дана в putNextEntry (копирование не производится).
Возможно, вам придется перевернуть его, прежде чем звонить в closeEntry.
Я не пробовал, так как вы можете попробовать намного быстрее.
I am not sure if this could save time and cpu having the deflated files saved in the database
Я не понимаю, но я настроен очень скептически.
OTOH ваша идея выглядит применимой также, когда вам дана куча файлов *.gz, и вы хотите сохранить их открытый текст без их повторного распаковки и сжатия.
Цель - как можно больше избегать сжатия и декомпрессии. Представьте себе огромный репозиторий, единственная функция которого - обслуживать большие наборы сжатых данных, из которых пользователи могут создавать свои собственные (большие) базы данных. Сжать один раз и использовать столько раз, сколько необходимо для создания динамически настраиваемых пакетов данных, а базы данных по запросу могут сэкономить память и объем обработки. Потоки сжатых данных короче, и время их обработки сокращается.
Формат zip не слишком сложен, поэтому вам нужно просто взять дефлированные данные и написать вокруг них свои собственные заголовки zip-файла. Формат задокументирован здесь. Если вы получаете дефлированные данные из файлов gzip, тогда у вас уже должны быть CRC и несжатые длины. (Если каждый файл gzip, который вы хотите преобразовать, состоит из одного потока deflate, то есть является одним членом gzip, и если длина несжатого файла гарантированно меньше 232 байтов, тогда вы можете удалить «следует» в этом операторе.)
Я «поигрался» с ним (см. Мое решение ниже) и получил черновой экспериментальный подход. Мне было интересно узнать о возможности того, что jdk рассмотрел сценарий, о котором я просил, чтобы был и «простой» и компактный способ его получения (т.е. просто указание «специального» метода в конфигурации ZipOutputSream или добавление или изменение нескольких предложения относительно фрагмента кода, которые обычно показаны в качестве примеров для сжатия простых файлов)
Наконец, я пошел дальше своего первого подхода, глубже проанализировал исходный код jdk и потратил некоторое время на его отладку и изменение:
https://gist.github.com/gylz/b2db94ce55f1829f2e2a2cd498092d46
https://gist.github.com/gylz/284d8b891fc0bbd3161d1ec5929be074
Если вы хотите попробовать, вы должны указать нужные пути в переменных PATH_ZIP_DIR, PATH_IN_DIR, PATH_TMP_DIR в классе Test. Файлы для сжатия берутся из каталога PATH_IN_DIR и zip-файла, созданного в PATH_ZIP_DIR. Тестовый класс короткий и не слишком сложный (несмотря на то, что он также является черновиком класса ExtraZipOutputStream). Я использовал простые текстовые файлы в PATH_IN_DIR, чтобы проверить их сжатие. Как вы можете видеть, в этом классе метод deflate () - это метод, который выполняет deflaton перед тем, как compress () поместит файлы в zip-файл (благодаря модифицированному ExtraZipOutputStream, использующему STORED, но записывающему метаданные, связанные с файлами, как если бы они были сброшены самим ExtraZipOutputStream.)
В блоке комментариев, добавленном к заголовку ExtraZipOutputStream.class, я объясняю, как определить изменения, которые я внес в исходный код.
Я знаю, что это устаревшая тема, но, заглянув в свои старые папки для очистки диска, я увидел «черновой» проект, который я сделал с этой идеей для моей работы. В конце концов, мы от него отказались, но я загрузил его в репо, если кому-то это может быть интересно ...
https://gitlab.com/gylz.mail/dynazip
Это даже не прототип, а всего лишь небольшой пример программы, показывающей, как это будет работать. Как сказал Марк Алдер, формат zip-архива не сложен, и он хорошо объясняется в ссылке, на которую он указал в своем ответе.
Я немного посмотрел на 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);
}
}
Привет, Гильермо, вы заметили мое решение от 6 сентября 2020 года в 20:42?