Estoy tratando de leer un solo archivo de un Java.util.Zip.ZipInputStream
, Y copiarlo en un Java.io.ByteArrayOutputStream
(Para que luego pueda crear un Java.io.ByteArrayInputStream
Y pasarlo a una biblioteca de terceros eso terminará cerrando la transmisión, y no quiero que mi ZipInputStream
se cierre).
Probablemente me falta algo básico aquí, pero nunca entro en 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 permitirá copiar la transmisión?
Editar:
Debería haber mencionado anteriormente que este ZipInputStream
no proviene de un archivo, por lo que no creo que pueda usar un ZipFile
. Proviene de un archivo cargado a través de un servlet.
Además, ya he llamado getNextEntry()
en el 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 simplemente paso el ZipInputStream
a mi biblioteca de terceros, la biblioteca cierra el transmisión, y no puedo hacer nada más, como tratar con los archivos restantes en la transmisión.
Su bucle parece válido: ¿qué devuelve el siguiente código (solo)?
zipStream.read(tempBuffer)
si devuelve -1, entonces 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 los datos de la entrada son significativos (es decir, getCompressedSize () devuelve un valor válido)? SI solo está leyendo un archivo Zip que no tiene entradas Zip de lectura anticipada incrustadas, entonces ZipInputStream no va a 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, está desplazada 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 Zip 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 deben ser correctos. Por el contrario, no es necesario proporcionar los valores incrustados en la secuencia.
Si usa ZipFile, lee la tabla al final de Zip. Si usa ZipInputStream, sospecho que getNextEntry () intenta usar las entradas incrustadas en la secuencia. Si no se especifican esos valores, ZipInputStream no tiene idea de cuánto tiempo puede durar la transmisión. El algoritmo de inflado se termina automáticamente (en realidad no es necesario conocer la longitud sin comprimir de la secuencia de salida para recuperar completamente la salida), pero es posible que la versión Java de este lector no maneja muy bien esta situación.
Diré que es bastante inusual que un servlet devuelva un ZipInputStream (es mucho más común recibir un inflatorInputStream si va a recibir contenido comprimido.
Probablemente intentaste leer de un FileInputStream
como este:
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 corriente.
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);
}
}
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) {
...
}
Usaría IOUtils del proyecto commons io.
IOUtils.copy(zipStream, byteArrayOutputStream);
Puede implementar su propio contenedor alrededor del ZipInputStream que ignora close () y pasarlo 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();
}
}
Llamaría a getNextEntry () en 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 ().
No está claro cómo obtuviste el zipStream. Debería funcionar cuando lo obtienes así:
zipStream = zipFile.getInputStream(zipEntry)
Por favor, intente el siguiente código
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();
}
no está claro cómo obtuviste el zipStream. Debería funcionar cuando lo obtienes así:
zipStream = zipFile.getInputStream(zipEntry)
Si está obteniendo el ZipInputStream de un ZipFile, puede obtener una secuencia para la biblioteca de fiestas en 3D, dejar que la use y obtener otra secuencia de entrada usando el código anterior.
Recuerde, un flujo de entrada es un cursor. Si tiene todos los datos (como un archivo Zip), puede solicitar N cursores sobre ellos.
Un caso diferente es si solo tiene un flujo de entrada "GZip", solo un flujo de bytes comprimido. En ese caso, el búfer ByteArrayOutputStream tiene sentido.