Чтение из 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) {
// ...
}
Чего мне не хватает, что позволит мне скопировать поток?
Редактировать:
Я должен был упомянуть ранее, что это ZipInputStream
не приходит из файла, поэтому я не думаю, что я могу использовать ZipFile
, Это происходит из файла, загруженного через сервлет.
Также я уже звонил getNextEntry()
на ZipInputStream
прежде чем перейти к этому фрагменту кода. Если я не пытаюсь скопировать файл в другой InputStream
(через OutputStream
выше), и просто передайте ZipInputStream
для моей сторонней библиотеки библиотека закрывает поток, и я больше ничего не могу сделать, например, иметь дело с оставшимися файлами в потоке.
10 ответов
Ваш цикл выглядит корректным - что возвращает следующий код (только сам по себе)?
zipStream.read(tempBuffer)
если он возвращает -1, то zipStream закрывается до того, как вы его получите, и все ставки отключены. Пришло время использовать ваш отладчик и убедиться, что то, что вам передают, действительно верно.
Когда вы вызываете getNextEntry(), возвращает ли оно значение, и значат ли данные в записи (т.е. возвращает ли getCompressedSize() действительное значение)? Если вы просто читаете Zip-файл, в который не встроены zip-записи с упреждающим чтением, то ZipInputStream не будет работать для вас.
Некоторые полезные моменты о формате Zip:
Каждый файл, встроенный в zip-файл, имеет заголовок. Этот заголовок может содержать полезную информацию (такую как сжатая длина потока, его смещение в файле, CRC) - или он может содержать некоторые магические значения, которые в основном говорят: "Информация не находится в заголовке потока, вы должны проверить Zip post-amble'.
Каждый zip-файл имеет таблицу, которая присоединяется к концу файла, который содержит все записи zip, а также реальные данные. Таблица в конце обязательна, а значения в ней должны быть правильными. Напротив, значения, встроенные в поток, не должны предоставляться.
Если вы используете ZipFile, он читает таблицу в конце почтового индекса. Если вы используете ZipInputStream, я подозреваю, что getNextEntry () пытается использовать записи, встроенные в поток. Если эти значения не указаны, ZipInputStream не знает, какой длины может быть поток. Алгоритм накачки является самоограниченным (на самом деле вам не нужно знать несжатую длину выходного потока, чтобы полностью восстановить вывод), но возможно, что Java-версия этого читателя не очень хорошо справляется с этой ситуацией.
Я скажу, что довольно необычно иметь сервлет, возвращающий ZipInputStream (гораздо чаще получать inflatorInputStream, если вы собираетесь получать сжатый контент.
Вы, вероятно, пытались читать с FileInputStream
как это:
ZipInputStream in = new ZipInputStream(new FileInputStream(...));
Это не сработает, поскольку zip-архив может содержать несколько файлов, и вам нужно указать, какой файл читать.
Вы можете использовать java.util.zip.ZipFile и такую библиотеку, как IOUtils из Apache Commons IO или ByteStreams из Guava, которые помогут вам в копировании потока.
Пример:
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 из общего проекта 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 () и т. Д.). Вызов getNextEntry() переместит "курсор" в начало возвращаемой записи. Затем используйте ZipEntry.getSize(), чтобы определить, сколько байтов вы должны прочитать, используя zipInputStream.read().
Пожалуйста, попробуйте код ниже
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();
}
Неясно, как вы получили zipStream. Это должно работать, когда вы получаете это так:
zipStream = zipFile.getInputStream(zipEntry)
Неясно, как вы получили ZipStream. Это должно работать, когда вы получаете это так:
zipStream = zipFile.getInputStream(zipEntry)
Если вы получаете ZipInputStream из ZipFile, вы можете получить один поток для сторонней библиотеки, позволить ей использовать его, и вы получите другой входной поток, используя код ранее.
Помните, что входной поток - это курсор. Если у вас есть все данные (например, ZipFile), вы можете запросить N курсоров поверх них.
Другой случай, если у вас есть только входной поток "GZip", только поток сжатых байтов. В этом случае ваш буфер ByteArrayOutputStream имеет смысл.
Проверьте, расположен ли входной поток в начале.
В противном случае, как реализация: я не думаю, что вам нужно записывать в поток результатов во время чтения, если вы не обработаете этот точный поток в другом потоке.
Просто создайте байтовый массив, прочитайте входной поток, затем создайте выходной поток.