java - Lectura de un ZipInputStream en un ByteArrayOutputStream
zipoutputstream (10)
Intento leer un solo archivo de java.util.zip.ZipInputStream
y copiarlo en java.io.ByteArrayOutputStream
(para poder crear un java.io.ByteArrayInputStream
y entregarlo a una biblioteca de terceros que terminará cerrando la transmisión, y no quiero que se ZipInputStream
mi ZipInputStream
).
Probablemente me falta algo básico aquí, pero nunca ingresé el ciclo while aquí:
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) {
// ...
}
¿Qué me estoy perdiendo que me permita copiar la transmisión?
Editar:
Debería haber mencionado antes que este ZipInputStream
no viene de un archivo, así que no creo que pueda usar un ZipFile
. Viene de un archivo cargado a través de un servlet.
Además, ya he llamado a getNextEntry()
en ZipInputStream
antes de llegar a este fragmento de código. Si no intento copiar el archivo en otro InputStream
(a través del OutputStream
mencionado anteriormente), y solo paso el ZipInputStream
a mi biblioteca de terceros, la biblioteca cierra la transmisión y no puedo hacer nada más, como tratar con el los archivos restantes en la secuencia.
No está claro cómo obtuviste el zipStream. Debería funcionar cuando lo obtienes así:
zipStream = zipFile.getInputStream(zipEntry)
No está claro cómo obtuvo ZipStream. Debería funcionar cuando lo obtienes así:
zipStream = zipFile.getInputStream(zipEntry)
Si está obteniendo ZipInputStream de un ZipFile, puede obtener una secuencia para la biblioteca de terceros, dejarla usar y obtener otra secuencia de entrada usando el código anterior.
Recuerde, un inputstream es un cursor. Si tiene toda la información (como un ZipFile) puede pedir N cursores sobre ella.
Un caso diferente es si solo tienes un inputtream "GZip", solo un flujo de bytes comprimido. En ese caso, tu buffer ByteArrayOutputStream tiene mucho sentido.
Verifique si el flujo de entrada está posicionado en el principio.
De lo contrario, como implementación: no creo que deba escribir en el flujo de resultados mientras está leyendo, a menos que procese esta secuencia exacta en otro hilo.
Simplemente cree una matriz de bytes, lea la secuencia de entrada y luego cree la secuencia de salida.
Usaría IOUtils del proyecto commons io.
IOUtils.copy(zipStream, byteArrayOutputStream);
Llamaría a getNextEntry () en el ZipInputStream hasta que esté en la entrada que desea (use ZipEntry.getName () etc.). Llamar a getNextEntry () avanzará el "cursor" al comienzo de la entrada que devuelve. Luego, use ZipEntry.getSize () para determinar cuántos bytes debe leer usando zipInputStream.read ().
Podría implementar su propio contenedor alrededor de ZipInputStream que ignore close () y entregarlo a la biblioteca de terceros.
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();
}
}
Su ciclo se ve válido: ¿qué devuelve el siguiente código (solo por sí mismo)?
zipStream.read(tempBuffer)
si está devolviendo -1, entonces el zipStream se cierra antes de que lo obtenga, y todas las apuestas están desactivadas. Es hora de usar su depurador y asegurarse de que lo que se le pasa sea realmente válido.
Cuando llama a getNextEntry (), ¿devuelve un valor y son significativos los datos en la entrada (es decir, get getStateSize () devuelve un valor válido)? Si solo está leyendo un archivo Zip que no tiene entradas zip de lectura anticipada incrustadas, entonces ZipInputStream no funcionará para usted.
Algunos datos útiles sobre el formato Zip:
Cada archivo incrustado en un archivo zip tiene un encabezado. Este encabezado puede contener información útil (como la longitud comprimida de la secuencia, su desplazamiento en el archivo, CRC), o puede contener algunos valores mágicos que básicamente dicen ''La información no está en el encabezado de la secuencia, debe verificar el Zip post-amble ''.
Cada archivo comprimido tiene una tabla adjunta al final del archivo que contiene todas las entradas zip, junto con los datos reales. La tabla al final es obligatoria y los valores que contiene deben ser correctos. Por el contrario, los valores incrustados en la secuencia no tienen que ser proporcionados.
Si usa ZipFile, lee la tabla al final del zip. Si usa ZipInputStream, sospecho que getNextEntry () intenta usar las entradas incrustadas en la transmisión. Si esos valores no están especificados, ZipInputStream no tiene idea de cuánto tiempo puede durar la transmisión. El algoritmo de inflado tiene terminación automática (en realidad no necesita conocer la longitud sin comprimir de la secuencia de salida para recuperar completamente la salida), pero es posible que la versión de Java de este lector no maneje esta situación muy bien.
Diré que es bastante inusual tener un servlet que devuelva ZipInputStream (es mucho más común recibir un inflatorInputStream si va a recibir contenido comprimido).
Por favor intente con el código abajo
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();
}
Probablemente haya intentado leer desde un FileInputStream
esta manera:
ZipInputStream in = new ZipInputStream(new FileInputStream(...));
Esto no funcionará, ya que un archivo zip puede contener varios archivos y debe especificar qué archivo leer.
Puede usar java.util.zip.ZipFile y una biblioteca como IOUtils de Apache Commons IO o ByteStreams de Guava que lo ayudan a copiar la transmisión.
Ejemplo:
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);
}
}
Te estás perdiendo llamada
Entrada ZipEntry = (ZipEntry) zipStream.getNextEntry ();
para posicionar el primer byte descomprimido de la primera entrada.
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) {
...
}