如何在java中定期刷新ZipOutputStream

时间:2011-08-18 13:17:42

标签: java memory zip

我正在尝试以zip格式存档文件列表,然后为用户即时下载...

下载1GB大小的邮政编码时,我面临内存不足问题

请帮助我如何在不增加jvm堆大小的情况下解决这个问题。我想定期冲洗流..

我正在尝试定期冲洗,但这不适合我。

请在下面找到我的代码:

try{
ServletOutputStream out = response.getOutputStream();
        ZipOutputStream zip = new ZipOutputStream(out);

        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition",
                "attachment; filename=\"ResultFiles.zip\"");
                  //adding multiple files to zip
        ZipUtility.addFileToZip("c:\\a", "print1.txt", zip);
ZipUtility.addFileToZip("c:\\a", "print2.txt", zip);
ZipUtility.addFileToZip("c:\\a", "print3.txt", zip);
ZipUtility.addFileToZip("c:\\a", "print4.txt", zip);

zip.flush();        
zip.close();
out.close();
} catch (ZipException ex) {
            System.out.println("zip exception");             
        } catch (Exception ex) {
            System.out.println("exception");
            ex.printStackTrace();   
}

public class ZipUtility {

    static public void addFileToZip(String path, String srcFile,
            ZipOutputStream zip) throws Exception {

        File file = new File(path + "\\" + srcFile);
        boolean exists = file.exists();
        if (exists) {

            long fileSize = file.length();
            int buffersize = (int) fileSize;
            byte[] buf = new byte[buffersize];

            int len;
            FileInputStream fin = new FileInputStream(path + "\\" + srcFile);
            zip.putNextEntry(new ZipEntry(srcFile));
            int bytesread = 0, bytesBuffered = 0;
            while ((bytesread = fin.read(buf)) > -1) {
                zip.write(buf, 0, bytesread);
                bytesBuffered += bytesread;
                if (bytesBuffered > 1024 * 1024) { //flush after 1mb
                    bytesBuffered = 0;
                    zip.flush();

                }
            }
            zip.closeEntry();
            zip.flush();
            fin.close();
        }

    }
}

}

4 个答案:

答案 0 :(得分:2)

您希望使用chunked编码来发送一个较大的文件,否则servlet容器将尝试在发送之前计算出您尝试发送的数据的大小,以便它可以设置Content-Length标头。由于您正在压缩文件,因此您不知道要发送的数据的大小。 Chunked-Encoding允许您以较小的块发送响应的片段。不要设置流的内容长度。您可以尝试使用curl或其他东西来查看从服务器获取的响应中的HTTP标头。如果它没有分块那么你就想弄明白了。您将要研究如何强制servlet容器发送分块编码。您可能必须将此添加到响应标头中,以使servlet容器将其发送到chunked。

response.setHeader("Transfer-Encoding", "chunked");

另一个选项是使用File.createTemp()将文件压缩为临时文件,然后发送该文件的内容。如果先压缩到临时文件,则可以知道文件的大小,并设置servlet的内容长度。

答案 1 :(得分:1)

我猜你正朝着错误的方向挖掘。尝试用文件流替换servlet输出流,看看问题是否仍然存在。我怀疑你的web容器在发送http头之前尝试收集整个servlet输出来计算内容长度。

答案 2 :(得分:1)

另一件事......你在try catch块中执行你的关闭。如果您有异常,这样就可以让流在文件上保持打开状态,并且不会让流有机会刷新到磁盘。

始终确保您的close处于finally块中(至少在您使用try-with-resources块获取Java 7之前)

//build the byte buffer for transferring the data from the file
//to the zip.
final int BUFFER = 2048;
byte [] data = new byte[BUFFER];

File zipFile= new File("C\:\\myZip.zip");

BufferedInputStream in = null;
ZipOutputStream zipOut = null;

try {
  //create the out stream to send the file to and zip it.
  //we want it buffered as that is more efficient.
  FileOutputStream destination = new FileOutputStream(zipFile);
  zipOut = new ZipOutputStream(new BufferedOutputStream(destination));
  zipOut.setMethod(ZipOutputStream.DEFLATED);

  //create the input stream (buffered) to read in the file so we
  //can write it to the zip.
  in = new BufferedInputStream(new FileInputStream(fileToZip), BUFFER);

  //now "add" the file to the zip (in object speak only).
  ZipEntry zipEntry = new ZipEntry(fileName);
  zipOut.putNextEntry(zipEntry);

  //now actually read from the file and write the file to the zip.
  int count;
  while((count = in.read(data, 0, BUFFER)) != -1) {
zipOut.write(data, 0, count);
  }
}
catch (FileNotFoundException e) {
  throw e;
}
catch (IOException e) {
  throw e;
}
finally {
 //whether we succeed or not, close the streams.
 if(in != null) {
try {
  in.close();
}
catch (IOException e) {
  //note and do nothing.
      e.printStackTrace();
}
 }
 if(zipOut != null) {
try {
      zipOut.close();
}
catch (IOException e) {
      //note and do nothing.
      e.printStackTrace();
    }
 }
}

现在,如果你需要循环,你可以绕过你需要添加更多文件的部分。也许传入一个文件数组并循环遍历它。这段代码可以帮我压缩文件。

答案 3 :(得分:1)

请勿根据文件大小调整buf的大小,请使用固定大小的缓冲区。

相关问题