generate - Descargar zip InputStream Struts2 Java
jsp download file (3)
El parámetro <param name="inputName">inputStream</param>
le dice a Struts2 de dónde obtener los bytes brutos que se enviarán al cliente. En su caso, desea enviar los bytes comprimidos. En su lugar, está configurando inputStream=ZipInputStream
, que es una secuencia que toma una fuente comprimida - para descomprimir el sitio. No quiere eso, quiere enviar los bytes comprimidos brutos.
REplace entonces
inputStream = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()))
por
inputStream = new ByteArrayInputStream(baos.toByteArray())
y debería funcionar
Primero, perdón por el inglés. Quiero saber si puedes ayudarme a resolver este problema. Lo que estoy intentando es descargar un zip que tengo creado desde múltiples bytes [] usando Struts2 y el resultado de la transmisión.
Uso ZipOutputStream y he logrado crear un archivo a partir de él y leerlo y descargarlo usando FileInputStream, pero mi problema es que no quiero crear un archivo. Solo quiero convertir ZipOutputStream en InputStream (por ejemplo en ZipIntputStream) y descargar ZipInputStream. Para hacer esto uso este código:
public void downloadZip() {
contentType = "application/octet-stream";
filename="myZip.zip";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes;
try {
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze;
bytes = otherClass.getBytes("File1");
ze = new ZipEntry("File1.pdf");
zos.putNextEntry(ze);
zos.write(bytes);
zos.closeEntry();
bytes = otherClass.getBytes("File2");
ze = new ZipEntry("File2.pdf");
zos.putNextEntry(ze);
zos.write(bytes);
zos.closeEntry();
zos.flush();
inputStream = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()));
zos.close();
}
catch(Exception e){...}
}
Mi acción struts.xml
<action...>
<result name="success" type="stream">
<param name="contentType">${contentType}</param>
<param name="inputName">inputStream</param>
<param name="contentDisposition">attachment;filename="${filename}"</param>
<param name="bufferSize">1024</param>
</result>
</action>
El problema es que el navegador me muestra un mensaje que me dice que el archivo no es un archivo zip válido, y su tamaño es de 0 bytes.
Espero haber explicado claramente, muchas gracias de antemano.
Editar: Como he comentado, finalmente obtengo la solución y es muy similar a la respuesta de leonbloy. Además de devolver ByteArrayInputStream , debería cerrar ZipOutputStream antes de crear ByteArrayInputStream . Este es el código de resultado, tal vez puede ser útil para otras personas:
...
zos.closeEntry();
zos.close();
inputStream = new ByteArrayInputStream(baos.toByteArray());
}
catch(Exception e){...}
}
Gracias por tu ayuda.
ZipInputStream es para descomprimir los archivos y acceder al contenido comprimido sin comprimir. Desea simplemente enviar los bytes comprimidos al navegador.
Después de crear el archivo zip, solo envíe los bytes del archivo utilizando response.getOutputStream(baos.toByteArray())
.
Si hace todo en memoria, corre el riesgo de quedarse sin memoria si está comprimiendo archivos grandes. Sería bueno limitar el tamaño de los archivos de entrada y dar al usuario un error si es demasiado grande.
Además, parece que inputStream es una variable de clase. Esta es una muy mala idea con los servlets. Básicamente, su clase no es segura para subprocesos y cuando dos usuarios acceden a la página al mismo tiempo, un usuario puede obtener el archivo zip del otro usuario. En su lugar, pase el objeto de respuesta como un parámetro al método o devuelva el baos a la persona que llama para que pueda escribir los bytes en la secuencia de respuesta correcta. Deshágase de las variables de clase que almacenan información específica para usuarios individuales y páselos entre llamadas.
Tienes suerte, esto es exactamente lo que necesitas .
En ese caso específico, he eludido el sistema de resultados del marco escribiendo directamente a la respuesta. De esta forma, el Zip se creará inmediatamente en el sistema cliente, y se alimentará progresivamente, en lugar de esperar al final de la elaboración y enviarlo todo de una vez con el resultado de Stream.
Entonces no se definió ningún resultado:
<action name="createZip" class="foo.bar.CreateZipAction" />
Y en la Acción:
public String execute() {
try {
/* Read the amount of data to be streamed from Database to File System,
summing the size of all Oracle''s BLOB, PostgreSQL''s ABYTE etc:
SELECT sum(length(my_blob_field)) FROM my_table WHERE my_conditions
*/
Long overallSize = getMyService().precalculateZipSize();
// Tell the browser is a ZIP
response.setContentType("application/zip");
// Tell the browser the filename, and that it needs to be downloaded instead of opened
response.addHeader("Content-Disposition", "attachment; filename=/"myArchive.zip/"");
// Tell the browser the overall size, so it can show a realistic progressbar
response.setHeader("Content-Length", String.valueOf(overallSize));
ServletOutputStream sos = response.getOutputStream();
ZipOutputStream zos = new ZipOutputStream(sos);
// Set-up a list of filenames to prevent duplicate entries
HashSet<String> entries = new HashSet<String>();
/* Read all the ID from the interested records in the database,
to query them later for the streams:
SELECT my_id FROM my_table WHERE my_conditions */
List<Long> allId = getMyService().loadAllId();
for (Long currentId : allId){
/* Load the record relative to the current ID:
SELECT my_filename, my_blob_field FROM my_table WHERE my_id = :currentId
Use resultset.getBinaryStream("my_blob_field") while mapping the BLOB column */
FileStreamDto fileStream = getMyService().loadFileStream(currentId);
// Create a zipEntry with a non-duplicate filename, and add it to the ZipOutputStream
ZipEntry zipEntry = new ZipEntry(getUniqueFileName(entries,fileStream.getFilename()));
zos.putNextEntry(zipEntry);
// Use Apache Commons to transfer the InputStream from the DB to the OutputStream
// on the File System; at this moment, your file is ALREADY being downloaded and growing
IOUtils.copy(fileStream.getInputStream(), zos);
zos.flush();
zos.closeEntry();
fileStream.getInputStream().close();
}
zos.close();
sos.close();
} catch (Exception e) {
logError(e);
finally {
IOUtils.closeQuietly(sos);
}
return NONE;
}