Подтвердить что ты не робот

Как создать многостраничный zip файл и прочитать его обратно?

Как я правильно zip байт в ByteArrayOutputStream, а затем прочитаю, что с помощью ByteArrayInputStream? У меня есть следующий метод:

private byte[] getZippedBytes(final String fileName, final byte[] input) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ZipOutputStream zipOut = new ZipOutputStream(bos);
    ZipEntry entry = new ZipEntry(fileName);
    entry.setSize(input.length);
    zipOut.putNextEntry(entry);
    zipOut.write(input, 0, input.length);
    zipOut.closeEntry();
    zipOut.close();

    //Turn right around and unzip what we just zipped
    ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()));

    while((entry = zipIn.getNextEntry()) != null) {
        assert entry.getSize() >= 0;
    }

    return bos.toByteArray();
}

Когда я выполняю этот код, утверждение внизу не выполняется, потому что entry.size - -1. Я не понимаю, почему выделенный объект не соответствует объекту, который был заархивирован.

4b9b3361

Ответ 1

Почему размер -1?

Вызов getNextEntry в ZipInputStream просто поместите курсор чтения в начало считываемой записи.

Размер (вместе с другими метаданными) сохраняется в конце фактических данных, поэтому он недоступен, когда курсор находится в начале.

Эта информация становится доступной только после того, как вы прочитаете все данные ввода или просто перейдете к следующей записи.

Например, переход к следующей записи:

// position at the start of the first entry
entry = zipIn.getNextEntry();
ZipEntry firstEntry = entry;    
// size is not yet available
System.out.println("before " + firstEntry.getSize()); // prints -1

// position at the start of the second entry
entry = zipIn.getNextEntry();
// size is now available
System.out.println("after " + firstEntry.getSize()); // prints the size

или чтение всех входных данных:

// position at the start of the first entry
entry = zipIn.getNextEntry();
// size is not yet available
System.out.println("before " + entry.getSize()); // prints -1

// read the whole entry data
while(zipIn.read() != -1);

// size is now available
System.out.println("after " + entry.getSize()); // prints the size

Ваше недоразумение довольно распространено, и есть ряд сообщений об ошибках относительно этой проблемы (которые закрыты как "Не проблема" ), например JDK- 4079029, JDK-4113731, JDK-6491622.

Как уже упоминалось в отчетах об ошибках, вы можете использовать ZipFile вместо ZipInputStream, который позволит получить информацию о размере до доступа к входным данным; но для создания ZipFile вам нужен File (см. конструкторы) вместо байтового массива.

Например:

File file = new File( "test.zip" );
ZipFile zipFile = new ZipFile(file);

Enumeration enumeration = zipFile.entries();
while (enumeration.hasMoreElements()) {
    ZipEntry zipEntry = (ZipEntry) enumeration.nextElement();
    System.out.println(zipEntry.getSize()); // prints the size
}

Как получить данные из входного потока?

Если вы хотите проверить, равны ли распакованные данные исходным входным данным, вы можете читать из входного потока следующим образом:

byte[] output = new byte[input.length];
entry = zipIn.getNextEntry();
zipIn.read(output);

System.out.println("Are they equal? " + Arrays.equals(input, output));

// and if we want the size
zipIn.getNextEntry(); // or zipIn.read();
System.out.println("and the size is " + entry.getSize());

Теперь output должен иметь тот же контент, что и input.

Ответ 2

Как закрепить byte[] и разархивировать его обратно?

Я обычно использую следующие методы для дефляции/раздувания (zip/unzip) small byte[] (т.е. когда он подходит в памяти). Он основан на примере, приведенном в Deflater javadoc и использует Deflater для сжатия данных и Inflater класс, чтобы распаковать его:

public static byte[] compress(byte[] source, int level) {
    Deflater compresser = new Deflater(level);
    compresser.setInput(source);
    compresser.finish();
    byte[] buf = new byte[1024];
    ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
    int n;
    while ((n = compresser.deflate(buf)) > 0)
        bos.write(buf, 0, n);
    compresser.end();
    return bos.toByteArray(); // You could as well return "bos" directly
}

public static byte[] uncompress(byte[] source) {
    Inflater decompresser = new Inflater();
    decompresser.setInput(source);
    byte[] buf = new byte[1024];
    ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
    try {
        int n;
        while ((n = decompresser.inflate(buf)) > 0)
            bos.write(buf, 0, n);
        return bos.toByteArray();
    } catch (DataFormatException e) {
        return null;
    } finally {
        decompresser.end();
    }
}

Нет необходимости в ByteArrayInputStream, но вы можете использовать InflaterInputStream, если вы действительно хотите ( но проще использовать Inflater проще).