【问题标题】:ZIP on the fly immediatelly即时压缩
【发布时间】:2010-11-27 07:02:58
【问题描述】:

我需要从 servlet 动态生成一个包含数据的 ZIP 文件。我将 ServletOutputSream 传递给 ZipOutputStream 构造函数,它会正确地将 ZIP 发送到客户端。当 ZIP 结果很大时,servlet 需要大量回答。我想首先创建 ZIP,然后发送它。

我创建了一个包装 ZipOutputStream 的辅助类。

我需要在调用flush方法时,立即发送已经生成的ZIP片段。我该怎么做?

public class ZIPResponse {

    private ZipOutputStream out;
    private HttpServletResponse response;
    private boolean isFirstEntry;
    private ConnectionManager con;

    public ZIPResponse(ConnectionManager con, OutputStream out) throws IOException {
        this.out = new ZipOutputStream(out);
        isFirstEntry = true;
        this.con = con;
    }

    public ZIPResponse(ConnectionManager con, HttpServletResponse response) throws IOException {
        this(con, response.getOutputStream());
        this.response = response;
        response.setCharacterEncoding("ISO-8859-1");
        sendNewEntry("con");
        flush();
    }

    public void sendNewEntry(String dataName) throws IOException {
        if (isFirstEntry) {
            isFirstEntry = false;
        } else {
            out.closeEntry();
        }
        out.putNextEntry(new ZipEntry(dataName));
    }

    public void sendData(String dataName, String data) throws IOException {
        sendNewEntry(dataName);
        sendData(data);
    }

    public void sendData(byte[] bytes) throws IOException {
        out.write(bytes);
        flush();
    }

    public void sendData(String data) throws IOException {
        sendData(data.getBytes());
    }

    public void sendAndCloseData(String dataName, String data) throws IOException {
        sendNewEntry(dataName);
        sendData(data);
        close();
    }

    private void sendDataFile(String dataName, DataTransformer dt)
            throws IOException, SQLException {
        sendNewEntry(dataName);
        dt.sendResultSet(this);
    }

    public void sendDataFile(String dataName, ResultSet data, String[] columnsRestriction) throws IOException,
            SQLException {
        RestrictedDataTransformer dt = new RestrictedDataTransformer(data, columnsRestriction);
        sendDataFile(dataName, dt);
    }

    public void sendDataFile(String dataName, ResultSet data) throws IOException,
            SQLException {
        DataTransformer dt = new DataTransformer(data);
        sendDataFile(dataName, dt);
    }

    public void sendDataFile(String dataName, PreparedStatement p) throws
            SQLException, IOException {
        ResultSet data = p.executeQuery();
        sendDataFile(dataName, data);
    }


    public void sendDataFile(String dataName, String SQL, String[] columnsRestricted)
            throws DBException, SQLException, IOException {
        ResultSetStatement st = new ResultSetStatement(con, SQL);
        ResultSet valores = st.executeQuery();
        try {
            sendDataFile(dataName, valores, columnsRestricted);
        } finally {
            valores.close();
        }
    }

    public void sendDataFile(String dataName, String SQL) throws DBException, SQLException, IOException {
        ResultSetStatement st = new ResultSetStatement(con, SQL);
        ResultSet valores = st.executeQuery();
        try {
            sendDataFile(dataName, valores);
        } finally {
            valores.close();
        }
    }

    public void sendAndCloseDataFile(String dataName, ResultSet data) throws
            SQLException, IOException {
        sendDataFile(dataName, data);
        close();
    }

    public void sendAndCloseDataFile(String dataName, String SQL) throws DBException, SQLException, IOException {
        sendDataFile(dataName, SQL);
        close();
    }

    public void close() throws IOException {
        sendNewEntry("OK");
        out.closeEntry();
        out.close();
        flush();
    }

    public void flush() throws IOException {       
      out.flush();
      response.flushBuffer();
    }
}

【问题讨论】:

  • 您的网络服务器(和客户端)需要支持发送“分块”响应,因为在发送数据之前不知道长度。

标签: java servlets zip


【解决方案1】:

这是我的实现:

String realpath = getServletContext().getRealPath("/");
    response.setContentType("application/zip");
    response.setHeader("Content-Disposition","attachment; filename="+fi.replace('/', '-')+"_"+ff.replace('/', '-')+".zip");
ServletOutputStream out = null;
ZipOutputStream zipfile = null;
try{
                List<Object[]> cfdis = /*my hibernate criteria source, your Database?*/

        out = response.getOutputStream();

        zipfile = new ZipOutputStream(out);
        ZipEntry zipentry = null;

        for(Object[] cfdi:cfdis){

            zipentry = new ZipEntry(cfdi[1].toString()+".xml");
            zipfile.putNextEntry(zipentry);

            InputStream in = new FileInputStream(new File(realpath+cfdi[0].toString()));


            byte[] bytes = new byte[FILEBUFFERSIZE];
            int bytesRead;
            while ((bytesRead = in.read(bytes)) != -1) {
                zipfile.write(bytes, 0, bytesRead);
            }
        }
}catch(...){}
finally{
   zipfile.close();
   out.close();
}

我希望你觉得这很有用。

【讨论】:

  • 我看不出这究竟是如何解决 OP 的问题,即 ZipOutputStream 在内存中完全缓冲到最后一位。你能详细说明一下吗?
  • 您没有将 zip 的内容存储在内存中(只是被垃圾收集的小字节 []),只有流,并且容器正在发送块,因为我们没有通过 ServletOutputStream 提供 ZipOutputStream 的响应长度(在 java ee 容器中定义的大小)。
【解决方案2】:

我猜您的问题是在完成 zip 创建之前将 zip 内容发送给响应。是的,你可以通过缓冲来做到这一点。

关于缓冲请关注Streaming large files in a java servlet

【讨论】:

    【解决方案3】:

    如果您的客户端对接收的内容很灵活,GZIP 将更好地用于流式压缩。

    【讨论】:

      猜你喜欢
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-07
      • 1970-01-01
      • 2011-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多