【问题标题】:Serving a zip file generated in server and stored in memory提供在服务器中生成并存储在内存中的 zip 文件
【发布时间】:2020-01-03 23:20:09
【问题描述】:

我需要使用基于 java 的 web 系统中的数据生成一堆 xml 文件,这些文件代表另一个基于 XML 的系统的完整导出。此类系统稍后将接受此导入。

我的方法是在内存中创建所有文件,然后将每个文件作为条目保存到内存中的 zip 文件中,稍后将其提供给客户端。

数据流运行良好,但不知何故,输出是一个空白文件。我想我错了 outpustream 结构

这是我可能会出错的部分: ...

 //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();

这是完整的代码:

@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();
            }
        }
    }

【问题讨论】:

    标签: java spring servlets server zip


    【解决方案1】:

    Zip 文件可能很大,所以不要在内存中生成它。直接写给客户。

    还有:

    • 不要设置Content-Type 三次。它只能有一个值。

    • 不要指定Content-Transfer-Encoding。这是一个电子邮件标头,而不是 HTTP 标头。

    • 由于您将进行流式传输,因此请勿指定 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();
    

    【讨论】:

    • 作为旁注,当请求完成时,我的客户不知何故被冻结了。文件下载完美,但我必须重新加载页面才能继续。我的请求处理一定有问题。
    • 在您的解决方案中,如何返回一个拉链的拉链?还是一次性拉上多个拉链?例如,我需要这样做:// headers must be set before streamingresponse.setContentType("application/zip");response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");``// stream straight to clientZipOutputStream zipped_out = new ZipOutputStream(response.getOutputStream());[...]zipped_out.finish(); 15 次点击一次,或者像这样提供一个带有 15 个拉链的拉链。
    • @Pra3t0r5 这听起来是一个很好的问题,你可以问。
    猜你喜欢
    • 2012-03-11
    • 2020-02-14
    • 1970-01-01
    • 2015-07-01
    • 2011-10-27
    • 1970-01-01
    • 2013-08-25
    • 2014-10-15
    • 1970-01-01
    相关资源
    最近更新 更多