Чтение из ZipInputStream в ByteArrayOutputStream

я пытаюсь прочитать один файл из java.util.zip.ZipInputStream, и скопируйте его в java.io.ByteArrayOutputStream (Так что я могу создать java.io.ByteArrayInputStream и передайте это сторонней библиотеке, которая в конечном итоге закроет поток, и я не хочу, чтобы мой ZipInputStream закрывается).

я, вероятно, пропустил что-то основное здесь, но я никогда не вхожу в цикл while здесь:

ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
int bytesRead;
byte[] tempBuffer = new byte[8192*2];
try {
    while ((bytesRead = zipStream.read(tempBuffer)) != -1) {
        streamBuilder.write(tempBuffer, 0, bytesRead);
    }
} catch (IOException e) {
    // ...
}

что мне не хватает, что позволит мне скопировать ручей?

Edit:

я должен был упомянуть раньше, что это ZipInputStream не из файла, поэтому я не думаю, что могу использовать ZipFile. Он поступает из файла, загруженного через сервлет.

кроме того, я уже позвонил getNextEntry() на ZipInputStream прежде чем перейти к этому фрагменту кода. Если я не попытаюсь скопировать файл в другой InputStream (через OutputStream упомянуто выше), и просто передайте ZipInputStream в мою стороннюю библиотеку, библиотека закрывает поток, и я больше ничего не могу сделать, например, иметь дело с оставшимися файлами в потоке.

10 ответов


ваш цикл выглядит допустимым - что возвращает следующий код (только по своему усмотрению)?

zipStream.read(tempBuffer)

если он возвращает -1, то zipStream закрывается до того, как вы его получите, и все ставки отключены. Пришло время использовать ваш отладчик и убедиться, что то, что передается вам, действительно допустимо.

когда вы вызываете getNextEntry (), возвращает ли он значение и значимы ли данные в записи (т. е. возвращает ли getCompressedSize () допустимое значение)? Если вы просто читаете Zip файл, в который не встроены записи zip для чтения, тогда ZipInputStream не будет работать для вас.

некоторые полезные лакомые кусочки о формате Zip:

каждый файл, встроенный в zip-файл, имеет заголовок. Этот заголовок может содержать полезную информацию (например, сжатую длину потока, смещение в файле, CRC) - или он может содержать некоторые магические значения, которые в основном говорят: "информация не находится в заголовке потока, вы должны проверить Zip постамбула'.

каждый zip-файл Затем имеет таблицу, которая прикреплена к концу файла, который содержит все записи zip, вместе с реальными данными. Таблица в конце обязательна, и значения в ней должны быть правильными. Напротив, значения, встроенные в поток, не должны предоставляться.

Если вы используете ZipFile, он считывает таблицу в конце zip. Если вы используете ZipInputStream, я подозреваю, что getNextEntry () пытается использовать записи, встроенные в поток. Если эти значения не указаны, то ZipInputStream понятия не имеет, как долго может быть поток. Алгоритм inflate самозакрывается (вам на самом деле не нужно знать несжатую длину выходного потока, чтобы полностью восстановить вывод), но возможно, что версия Java этого читателя не очень хорошо справляется с этой ситуацией.

Я скажу, что довольно необычно иметь сервлет, возвращающий ZipInputStream (гораздо чаще получать inflatorInputStream, если вы собираетесь получать сжатый контент.


вы, вероятно, пробовали читать из FileInputStream такой:

ZipInputStream in = new ZipInputStream(new FileInputStream(...));

этой не работа с zip-архив может содержать несколько файлов и нужно указать какой файл для чтения.

можно использовать java.утиль.застежка-молния.Zip-файл и библиотека, такая как IOUtils из Apache Commons IO или ByteStreams из гуавы это поможет вам в копировании потока.

пример:

ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipFile zipFile = new ZipFile("foo.zip")) {
    ZipEntry zipEntry = zipFile.getEntry("fileInTheZip.txt");

    try (InputStream in = zipFile.getInputStream(zipEntry)) {
        IOUtils.copy(in, out);
    }
}

Я хотел бы использовать IOUtils из проекта commons io.

IOUtils.copy(zipStream, byteArrayOutputStream);

вы пропускаете вызов

zipentry entry = (ZipEntry) zipStream.getNextEntry ();

чтобы расположить первый байт, распакованный из первой записи.

 ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
 int bytesRead;
 byte[] tempBuffer = new byte[8192*2];
 ZipEntry entry = (ZipEntry) zipStream.getNextEntry();
 try {
     while ( (bytesRead = zipStream.read(tempBuffer)) != -1 ){
        streamBuilder.write(tempBuffer, 0, bytesRead);
     }
 } catch (IOException e) {
      ...
 }

вы можете реализовать свою собственную оболочку вокруг ZipInputStream, который игнорирует close () и передать это сторонней библиотеке.

thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream));


class CloseIgnoringInputStream extends InputStream
{
    private ZipInputStream stream;

    public CloseIgnoringInputStream(ZipInputStream inStream)
    {
        stream = inStream;
    }

    public int read() throws IOException {
        return stream.read();
    }

    public void close()
    {
        //ignore
    }

    public void reallyClose() throws IOException
    {
        stream.close();
    }
}

Я бы вызвал getNextEntry () на ZipInputStream, пока он не будет в нужной записи (используйте ZipEntry.getName () etc.). Вызов getNextEntry () переместит "курсор" в начало возвращаемой записи. Затем, используйте ZipEntry.getSize (), чтобы определить, сколько байтов вы должны прочитать с помощью zipInputStream.читать.)(


неясно, как вы получили zipStream. Он должен работать, когда вы получаете его так:

  zipStream = zipFile.getInputStream(zipEntry)

t неясно, как вы получили zipStream. Он должен работать, когда вы получаете его так:

  zipStream = zipFile.getInputStream(zipEntry)

Если вы получаете ZipInputStream из ZipFile, вы можете получить один поток для библиотеки 3D party, позвольте ему использовать его, и вы получите другой входной поток, используя код раньше.

помните, что inputstream-это курсор. Если у вас есть все данные (например, ZipFile), вы можете попросить N курсоров над ним.

другой случай, если у вас есть только " GZip" inputstream, только сжатый поток байтов. В этом случае буфер bytearrayoutputstream имеет смысл.


пожалуйста, попробуйте ниже код

private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException {

  BufferedInputStream buffer = null;
  FileInputStream fileStream = null;
  ByteArrayOutputStream byteOut = null;
  byte data[] = new byte[BUFFER];

  try {
   try {
    fileStream = new FileInputStream(zipName);
    buffer = new BufferedInputStream(fileStream);
    byteOut = new ByteArrayOutputStream();

    int count;
    while((count = buffer.read(data, 0, BUFFER)) != -1) {
     byteOut.write(data, 0, count);
    }
   } catch(Exception e) {
    throw new WorkflowServiceBusinessException(e.getMessage(), e);
   } finally {
    if(null != fileStream) {
     fileStream.close();
    }
    if(null != buffer) {
     buffer.close();
    }
    if(null != byteOut) {
     byteOut.close();
    }
   }
  } catch(Exception e) {
   throw new WorkflowServiceBusinessException(e.getMessage(), e);
  }
  return byteOut.toByteArray();

 }

Проверьте, расположен ли входной поток в попрошайничестве.

в противном случае, как реализация: я не думаю, что вам нужно писать в поток результатов во время чтения, если вы не обрабатываете этот точный поток в другом потоке.

просто создайте массив байтов, прочитайте входной поток, затем создайте выходной поток.