【问题标题】:Creation of single zip containing multiple zips fails for ServletOutputStreamServletOutputStream 创建包含多个 zip 的单个 zip 失败
【发布时间】:2014-09-08 07:37:39
【问题描述】:

我正在使用 commons compress 压缩多个文件并将其从 Servlet 发送到客户端。 这些文件可以是任何类型的文件(文本、视频、音频、档案、图像等)的组合。我使用 IOUtils.copy(is, os) 获取文件的 inputStream 并写入 ServletOutputStream。 该代码通常适用于任何文档组合,但是当请求下载包含超过 1 个 zip 的文件时,我得到 java.io.IOException: Closed 结果,即使 zip 的大小是各个文件大小的总和(我没有使用压缩),创建的 zip 文件也会损坏。

我尝试在本地创建 zip 并在 ZipArchiveOutputStream 的构造函数中使用 FileOutputStream 而不是 response.getOutputStream(),它成功。 所以,ServletOutputStream 似乎存在问题。

任何人都可以提出任何解决方法。

这是我的代码:

`try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream( response.getOutputStream())) {
        //get fileList
        for(File file : files) {
           addFileToZip(zos, file.getName(), new BufferedInputStream(new FileInputStream(file)));
        }
        zos.close()
    }

`

public static void addFileToZip(ZipArchiveOutputStream zipOutputStream, String filename, InputStream inputStream) throws FileNotFoundException {
    if(zipOutputStream != null && inputStream != null) {
        try {
            zipOutputStream.putArchiveEntry(new ZipArchiveEntry(filename));
            IOUtils.copy(inputStream, zipOutputStream);
            logger.debug("fileAddedToZip :" + filename);
        } catch (IOException e) {
            logger.error("Error in adding file :" + filename, e); 
        } finally {
            try {
                inputStream.close();
                zipOutputStream.closeArchiveEntry(); //**Starts to fail here after 1st zip is added**
            } catch (IOException e) {
                logger.error("Error in closing zip entry :" + filename, e);
            }
        }
    }
`

这是异常跟踪: `

java.io.IOException: Closed
        at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:627)
        at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:577)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1287)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1272)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeDataDescriptor(ZipArchiveOutputStream.java:997)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.closeArchiveEntry(ZipArchiveOutputStream.java:461)
        at xxx.yyy.zzz.util.ZipUtils.addFileToZip(ZipUtils.java:110)

第 110 行是zipOutputStream.closeArchiveEntry(); //**Starts to fail here after 1st zip is added**

提前致谢。

【问题讨论】:

  • 什么是 inputStream 以及为什么在 addFileToZip() 中关闭它?
  • inputStream 是我要添加的 zip 的 FileInputStream。复制后,我将关闭它以释放其资源。
  • 除了删除无关的zos.close(),您可以在closeArchiveEntry() 之前尝试zipOutputStream.flush(),因为我看不出其他任何错误。
  • 试过 zipOutputStream.flush() 但它不起作用。此外,任何其他文件类型组合的 zip 创建都是成功的,除非文件有 1 个以上的 zip 文件。
  • 这里有人有解决方案吗?

标签: java zip apache-commons-compress


【解决方案1】:

问题是您使用 try-with-resources 自动关闭您在其中创建的流,但您也手动关闭它,因此当 JVM 尝试自动关闭时,您会收到 java.io.IOException: Closed 异常,因为它已经关闭了。

如果您使用try-with-resources,则无需关闭您在其中创建的流。删除您的手册zos.close() 声明:

try (ZipArchiveOutputStream zos =
         new ZipArchiveOutputStream(response.getOutputStream())) {

    //get fileList
    for(File file : files) {
       addFileToZip(zos, attachment.getFileName(), is);
    }
} // Here zos will be closed automatically!

还要注意,一旦zos 关闭,它也会关闭servlet 的底层OutputStream,因此您将无法添加更多条目。您必须在关闭之前添加所有内容。

【讨论】:

  • 我编辑了我的代码以显示右括号的正确位置以进行尝试。如您所见,zos 的自动关闭只会在我添加完所有文件后才会发生。但是,这里的问题是第一次之后的任何后续 zip 添加都开始失败(在第二个代码 sn-p 中查找我的评论)。
  • 您是否正确创建了传递给您的addFileToZip() 方法的is 输入流?请发布您的所有代码。
  • 编辑了代码。 @icza 这里的问题不是我如何获取流,而是为什么代码适用于 所有 其他情况和文件组合,但包含超过 1 个 zip 的情况除外。为什么调用zipOutputStream.closeArchiveEntry() 在初始输入后开始失败?我怎样才能使它适用于超过 1 个 zip?
  • 请发布异常堆栈跟踪并指出它在您的代码中的哪一行。
  • try 阻塞zos 流之后,servlet 的输出流被关闭。之后您不会尝试添加更多条目,对吧?
猜你喜欢
  • 2017-02-07
  • 2016-03-16
  • 2015-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多