Serving a zip file generated in server and stored in memory

I need to generate a bunch of xml files with data from my java based web system, that represent a whole export of another system based in XML. Such system will accept this import later.

My approach is to create all files in memory, then save each one as entries it to a zip also in memory, wich later is served to the client.

The data flow is working perfectly, but somehow the output is a blank file. I think i got wrong the outpustream structure

This is the part that i might be getting wrong: …

 //ZIP creation in server memory
                ByteArrayOutputStream datosEnMemoria = new ByteArrayOutputStream();
                ZipOutputStream zipped_out = new ZipOutputStream(datosEnMemoria)

                    //close and zip entry
                    xmlData.append(Tangonet.terminarCargaRegistros());
                    byte[] xmlBinData = xmlData.toString().getBytes();
                    zipped_out.write(xmlBinData, 0, xmlBinData.length);
                    zipped_out.closeEntry();
                }
                byte[] zipped_out_size = zipped_out.toString().getBytes();

                response.setContentType("application/octet-stream");
                response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");
                response.setHeader("Content-length", "" + zipped_out_size.length);
                response.setHeader("Content-Type", "application/zip");
                response.setHeader("Content-Type", "application/octet-stream");
                response.setHeader("Content-Transfer-Encoding", " binary");
                //closing zip and send it to client
                zipped_out.flush();
                zipped_out.close();
//                out.flush();
//                out.close();

This is the full code:

@RequestMapping(value = "/cierreZ/exportar", method = RequestMethod.GET)
    public void cierreZExportar(@ModelAttribute InformesFinancierosForm informesFinancierosForm, HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession(true);
        String fechaInicio = null;
        String fechaFin = null;

        if (session.getAttribute("mesActual") != null) {
            informesFinancierosForm.setFechaInicio("01-" + informesFinancierosForm.getMes());
            informesFinancierosForm.setFechaFin(new SimpleDateFormat("dd-MM-yyyy").format(DateUtil.getUltimoDiaDelMes(DateUtil.traduceDateDate((String) session.getAttribute("fechaIni")))));
            fechaInicio = informesFinancierosForm.getFechaInicio();
            fechaFin = informesFinancierosForm.getFechaFin();
        } else {
            fechaInicio = (String) session.getAttribute("fechaIni");
            fechaFin = (String) session.getAttribute("fechaFin");
        }

        if (informeService.isRangoFechaValido(informesFinancierosForm.getSalasSeleccionadas(), fechaInicio)) {
            if (!(fechaInicio.compareTo("") == 0) || (fechaFin.compareTo("") == 0)
                    || informesFinancierosForm.getSalasSeleccionadas().length == 0) {

//                ServletOutputStream out = response.getOutputStream();
                List<InformeCierreZItemForm> listadoInfCierreZ = cierreZService.getCierres(informesFinancierosForm.getSalasSeleccionadas(), fechaInicio, fechaFin);

                //ZIP creation in server memory
                ByteArrayOutputStream datosEnMemoria = new ByteArrayOutputStream();
                ZipOutputStream zipped_out = new ZipOutputStream(datosEnMemoria);

                //filling zip with static xml files
                for (int i = 0; i < Tangonet.documentos_estaticos_tangonet.length; i++) {
                    ZipEntry xmlFile = new ZipEntry(Tangonet.documentos_estaticos_tangonet[i][0] + ".xml");
                    zipped_out.putNextEntry(xmlFile);

                    StringBuilder xmlData = new StringBuilder();
                    xmlData.append(Tangonet.documentos_estaticos_tangonet[i][1]);

                    byte[] xmlBinData = xmlData.toString().getBytes();
                    zipped_out.write(xmlBinData, 0, xmlBinData.length);
                    zipped_out.closeEntry();
                }

                //filling zip with dynamic xml files
                for (int i = 0; i < Tangonet.documentos_dinamicos_tangonet.length; i++) {

                    //dynamic xml creation
                    ZipEntry xmlFile = new ZipEntry(Tangonet.documentos_dinamicos_tangonet[i][0] + ".xml");
                    zipped_out.putNextEntry(xmlFile);

                    //xml schema
                    StringBuilder xmlData = new StringBuilder();
                    xmlData.append(Tangonet.documentos_dinamicos_tangonet[i][1]);

                    //xml data rows
                    for (InformeCierreZItemForm informeCierreZActual : listadoInfCierreZ) {
                        Sala salaActual = informeCierreZActual.getSala();
                        CierrezList CierresZ = cierreZService.getCierresZ(salaActual, fechaInicio, fechaFin);

                        //fiscal data in rows
                        Tangonet datosFiscalesCierrezActual = tangonetDatos.getDatosFiscales(salaActual);

                        for (Cierrez cierreActual : CierresZ) {
                            if (Tangonet.documentos_dinamicos_tangonet[i][0].equals("Comp_de_Facturación_para_Cobranza_Centralizada___GVA12")) {
                                xmlData.append(datosFiscalesCierrezActual.crearRegistroGVA12(cierreActual));
                            } else {
                                xmlData.append(datosFiscalesCierrezActual.crearRegistroGVA42(cierreActual));
                            }
                        }
                    }
                    //close and zip entry
                    xmlData.append(Tangonet.terminarCargaRegistros());
                    byte[] xmlBinData = xmlData.toString().getBytes();
                    zipped_out.write(xmlBinData, 0, xmlBinData.length);
                    zipped_out.closeEntry();
                }
                byte[] zipped_out_size = zipped_out.toString().getBytes();

                response.setContentType("application/octet-stream");
                response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");
                response.setHeader("Content-length", "" + zipped_out_size.length);
                response.setHeader("Content-Type", "application/zip");
                response.setHeader("Content-Type", "application/octet-stream");
                response.setHeader("Content-Transfer-Encoding", " binary");
                //closing zip and send it to client
                zipped_out.flush();
                zipped_out.close();
//                out.flush();
//                out.close();
            }
        }
    }

Answer

Zip file can be big, so don’t generate it in memory. Write it straight to client.

Also:

  • Don’t set Content-Type three times. It can only have one value.

  • Don’t specify Content-Transfer-Encoding. It’s an email header, not an HTTP header.

  • Since you’ll be streaming, don’t specify Content-length.

// headers must be set before streaming
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");

// stream straight to client
ZipOutputStream zipped_out = new ZipOutputStream(response.getOutputStream());

// Add zip entries and data here:
// Loop:
//    zipped_out.putNextEntry(...)
//    Generate XML, writing it straight to zipped_out
//      Remember to flush any streams/writers wrapped around zipped_out
//      Do not close zipped_out or wrappers of it
//        If that cannot be prevented, use a CloseShieldOutputStream (from Commons IO)
//    No need to call zipped_out.closeEntry()

// make sure to finish the zip stream
zipped_out.finish();

Leave a Reply

Your email address will not be published. Required fields are marked *