首页 文章

从ZipInputStream读取到ByteArrayOutputStream

提问于
浏览
17

我试图从 java.util.zip.ZipInputStream 读取一个文件,并将其复制到 java.io.ByteArrayOutputStream (这样我就可以创建一个 java.io.ByteArrayInputStream 并将其交给第三方库,最终关闭流,我不想要我的 ZipInputStream 越来越近了 .

我可能在这里遗漏了一些基本内容,但我从未在这里输入while循环:

ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
int bytesRead;
byte[] tempBuffer = new byte[8192*2];
try {
    while ((bytesRead = zipStream.read(tempBuffer)) != -1) {
        streamBuilder.write(tempBuffer, 0, bytesRead);
    }
} catch (IOException e) {
    // ...
}

我错过了哪些可以让我复制流?

Edit:

我之前应该提到这个 ZipInputStream 不是来自文件,所以我认为我不能使用 ZipFile . 它来自通过servlet上传的文件 .

另外,在获取这段代码之前,我已经在 ZipInputStream 上调用了 getNextEntry() . 如果我不尝试将文件复制到另一个 InputStream (通过上面提到的 OutputStream ),并且只是将 ZipInputStream 传递给我的第三方库,那么库将关闭流,而我无法做更多的事情,比如处理流中的剩余文件 .

10 回答

  • 1

    你的循环看起来有效 - 下面的代码(只是它自己的)会返回什么?

    zipStream.read(tempBuffer)
    

    如果它返回-1,那么zipStream会在你获得之前关闭,所有的赌注都会关闭 . 是时候使用你的调试器,并确保传递给你的是真正有效的 .

    当你调用getNextEntry()时,它是否返回一个值,并且条目中的数据是否有意义(即getCompressedSize()是否返回有效值)?如果您只是阅读没有嵌入预读zip条目的Zip文件,那么ZipInputStream将不适合您 .

    关于Zip格式的一些有用的花絮:

    嵌入在zip文件中的每个文件都有一个 Headers . 此标头可以包含有用的信息(例如流的压缩长度,它在文件中的偏移量,CRC) - 或者它可以包含一些基本上说'信息不在流 Headers 中的魔术值,你必须检查Zip post-amble' .

    然后每个zip文件都有一个附加到文件末尾的表,其中包含所有zip条目以及实际数据 . 最后的表是必需的,其中的值必须正确 . 相反,不必提供流中嵌入的值 .

    如果您使用ZipFile,它会读取zip末尾的表格 . 如果您使用ZipInputStream,我怀疑getNextEntry()尝试使用流中嵌入的条目 . 如果未指定这些值,则ZipInputStream不知道流可能有多长 . inflate算法是自终止的(你实际上不需要知道输出流的未压缩长度以便完全恢复输出),但是这个读者的Java版本可能不能很好地处理这种情况 .

    我会说让一个servlet返回ZipInputStream是相当不寻常的(如果你要接收压缩内容,接收inflatorInputStream会更常见 .

  • 0

    您可能尝试过这样阅读 FileInputStream

    ZipInputStream in = new ZipInputStream(new FileInputStream(...));
    

    这个 won’t 工作,因为zip存档可以包含多个文件,您需要指定要读取的文件 .

    您可以使用java.util.zip.ZipFile和诸如IOUtils from Apache Commons IOByteStreams from Guava之类的库来帮助您复制流 .

    例:

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try (ZipFile zipFile = new ZipFile("foo.zip")) {
        ZipEntry zipEntry = zipFile.getEntry("fileInTheZip.txt");
    
        try (InputStream in = zipFile.getInputStream(zipEntry)) {
            IOUtils.copy(in, out);
        }
    }
    
  • 7

    我会从公共项目中使用IOUtils .

    IOUtils.copy(zipStream, byteArrayOutputStream);
    
  • 4

    你错过了电话

    ZipEntry entry =(ZipEntry)zipStream.getNextEntry();

    定位第一个条目解压缩的第一个字节 .

    ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
     int bytesRead;
     byte[] tempBuffer = new byte[8192*2];
     ZipEntry entry = (ZipEntry) zipStream.getNextEntry();
     try {
         while ( (bytesRead = zipStream.read(tempBuffer)) != -1 ){
            streamBuilder.write(tempBuffer, 0, bytesRead);
         }
     } catch (IOException e) {
          ...
     }
    
  • 0

    您可以在ZipInputStream周围实现自己的包装器,忽略close()并将其移交给第三方库 .

    thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream));
    
    
    class CloseIgnoringInputStream extends InputStream
    {
        private ZipInputStream stream;
    
        public CloseIgnoringInputStream(ZipInputStream inStream)
        {
            stream = inStream;
        }
    
        public int read() throws IOException {
            return stream.read();
        }
    
        public void close()
        {
            //ignore
        }
    
        public void reallyClose() throws IOException
        {
            stream.close();
        }
    }
    
  • 6

    我会在ZipInputStream上调用getNextEntry(),直到它位于您想要的条目(使用ZipEntry.getName()等) . 调用getNextEntry()会将“游标”前进到它返回的条目的开头 . 然后,使用ZipEntry.getSize()确定使用zipInputStream.read()读取的字节数 .

  • 3

    目前还不清楚你是如何得到zipStream的 . 当你得到它时,它应该工作:

    zipStream = zipFile.getInputStream(zipEntry)
    
  • -1

    我不清楚你是如何得到zipStream的 . 当你得到它时,它应该工作:

    zipStream = zipFile.getInputStream(zipEntry)
    

    如果从ZipFile获取ZipInputStream,您可以获得3d方库的一个流,让它使用它,然后使用之前的代码获取另一个输入流 .

    请记住,输入流是一个游标 . 如果您拥有整个数据(如ZipFile),您可以请求N个游标 .

    不同的情况是,如果您只有一个“GZip”输入流,只有一个压缩字节流 . 在这种情况下,你ByteArrayOutputStream缓冲区是有道理的 .

  • 0

    请尝试下面的代码

    private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException {
    
      BufferedInputStream buffer = null;
      FileInputStream fileStream = null;
      ByteArrayOutputStream byteOut = null;
      byte data[] = new byte[BUFFER];
    
      try {
       try {
        fileStream = new FileInputStream(zipName);
        buffer = new BufferedInputStream(fileStream);
        byteOut = new ByteArrayOutputStream();
    
        int count;
        while((count = buffer.read(data, 0, BUFFER)) != -1) {
         byteOut.write(data, 0, count);
        }
       } catch(Exception e) {
        throw new WorkflowServiceBusinessException(e.getMessage(), e);
       } finally {
        if(null != fileStream) {
         fileStream.close();
        }
        if(null != buffer) {
         buffer.close();
        }
        if(null != byteOut) {
         byteOut.close();
        }
       }
      } catch(Exception e) {
       throw new WorkflowServiceBusinessException(e.getMessage(), e);
      }
      return byteOut.toByteArray();
    
     }
    
  • 4

    检查输入流是否位于乞讨中 .

    否则,作为实现:我不认为你在阅读时需要写入结果流,除非你在另一个线程中处理这个确切的流 .

    只需创建一个字节数组,读取输入流,然后创建输出流 .

相关问题