Я пытаюсь прочитать один файл из 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 в свою стороннюю библиотеку, библиотека закроет поток, и я больше ничего не смогу сделать, например, с оставшиеся файлы в потоке.
А для копирования InputStream в OutputStream есть служебный класс Streams в библиотеке commons-fileupload (Apache). Вы выполняете Streams.copy (вход, выход, закрытие?), И все готово.
Так что же возвращает zipEntry.getSize ()?
В этом случае zipEntry.getSize () возвращает разумное число, 28689.




Проверьте, расположен ли входной поток в попрошайничестве.
В противном случае, как реализация: я не думаю, что вам нужно писать в поток результатов во время чтения, если вы не обрабатываете этот точный поток в другом потоке.
Просто создайте массив байтов, прочтите входной поток, затем создайте выходной поток.
Непонятно, откуда у вас zipStream. Это должно работать, когда вы получите это так:
zipStream = zipFile.getInputStream(zipEntry)
Я только что добавил пояснение по этому поводу, но оно исходит не из файла.
Я бы использовал IOUtils из проекта commons io.
IOUtils.copy(zipStream, byteArrayOutputStream);
Похоже, это может сработать. Я попробую завтра, когда пойду на работу. Спасибо.
Вы, наверное, пробовали читать с FileInputStream вот так:
ZipInputStream in = new ZipInputStream(new FileInputStream(...));Этот не будет работает, поскольку zip-архив может содержать несколько файлов, и вам нужно указать, какой файл читать.
Вы можете использовать java.util.zip.ZipFile и такую библиотеку, как 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);
}
}
Непонятно, откуда у вас zipStream. Это должно работать, когда вы получите это так:
zipStream = zipFile.getInputStream(zipEntry)
Если вы получаете ZipInputStream из ZipFile, вы можете получить один поток для сторонней библиотеки, позволить ему использовать его, и вы получите другой входной поток, используя предыдущий код.
Помните, что поток ввода - это курсор. Если у вас есть все данные (например, ZipFile), вы можете запросить N курсоров поверх них.
Другой случай - если у вас есть только поток ввода "GZip", только поток байтов в формате zip. В этом случае буфер ByteArrayOutputStream имеет смысл.
Я бы вызвал getNextEntry () в ZipInputStream до тех пор, пока он не окажется в нужной записи (используйте ZipEntry.getName () и т. д.). Вызов getNextEntry () переместит «курсор» в начало возвращаемой записи. Затем используйте ZipEntry.getSize (), чтобы определить, сколько байтов вы должны прочитать с помощью zipInputStream.read ().
На самом деле я вызвал getNextEntry () перед тем, как перейти к этому фрагменту. Я просто добавил пояснение к вопросу.
Вы можете реализовать свою собственную оболочку вокруг 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();
}
}
Ваш цикл выглядит допустимым - что возвращает следующий код (только сам по себе)?
zipStream.read(tempBuffer)
если он возвращает -1, то zipStream закрывается до того, как вы его получите, и все ставки отключены. Пришло время использовать ваш отладчик и убедиться, что то, что вам передается, действительно верно.
Когда вы вызываете getNextEntry (), возвращает ли он значение и являются ли данные в записи значимыми (т.е. возвращает ли getCompressedSize () допустимое значение)? ЕСЛИ вы просто читаете Zip-файл, в который не встроены zip-записи с упреждающим чтением, ZipInputStream не подойдет вам.
Некоторые полезные факты о формате Zip:
Каждый файл, встроенный в zip-файл, имеет заголовок. Этот заголовок может содержать полезную информацию (такую как сжатая длина потока, его смещение в файле, CRC) - или он может содержать некоторые магические значения, которые в основном говорят: `` Информация отсутствует в заголовке потока, вы должны проверить почтовый индекс '.
Затем каждый zip-файл имеет таблицу, которая прикрепляется к концу файла, которая содержит все записи zip вместе с реальными данными. Таблица в конце является обязательной, и значения в ней должны быть правильными. Напротив, значения, встроенные в поток, указывать не нужно.
Если вы используете ZipFile, он читает таблицу в конце zip. Если вы используете ZipInputStream, я подозреваю, что getNextEntry () пытается использовать записи, встроенные в поток. Если эти значения не указаны, ZipInputStream не знает, какой длины может быть поток. Алгоритм inflate самозавершается (на самом деле вам не нужно знать несжатую длину выходного потока, чтобы полностью восстановить выходные данные), но возможно, что версия этого читателя для Java не очень хорошо справляется с этой ситуацией.
Я скажу, что довольно необычно иметь сервлет, возвращающий ZipInputStream (гораздо чаще получать inflatorInputStream, если вы собираетесь получать сжатый контент.
ZipInputStream в java НЕ РАБОТАЕТ С ЭТОМ ХОРОШО. Спасибо за публикацию.
Пожалуйста, попробуйте код ниже
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();
}
Вы пропустили звонок
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) {
...
}
Возможно, вам сейчас все равно, но вы можете избежать копирования всех данных и избегайте закрытия сторонней библиотекой потока, если вы оберните исходный входной поток (zipStream) и переопределите метод закрытия. 1) Сделайте открытый класс DontCloseInputStream расширением FilterInputStream. 2) Создайте конструктор (InputStream in), который вызывает super (in) 3) Замените метод закрытия и ничего не делайте 4) Создайте новый DontCloseInputStream (zipStream) 5) передайте его в библиотеку. И voi lá