从 zip 文件夹中解压单个文件而不写入磁盘

Decompress a single file from a zip folder without writing in disk

是否可以从 zip 文件夹中解压单个文件并 return 解压后的文件而不将数据存储在服务器上?

我有一个结构未知的 zip 文件,我想开发一种服务,可以按需提供给定文件的内容,而无需解压缩整个 zip 文件,也无需写入磁盘。 所以,如果我有这样的 zip 文件

zip_folder.zip
  |  folder1
       |  file1.txt
       |  file2.png
  |  folder 2
       |  file3.jpg
       |  file4.pdf
  |  ...

所以,我希望我的服务能够接收文件的名称和路径,以便我可以发送文件。

例如,fileName 可以是 folder1/file1.txt

 def getFileContent(fileName: String): IBinaryContent = {
    val content: IBinaryContent = getBinaryContent(...)

    val zipInputStream: ZipInputStream = new ZipInputStream(content.getInputStream)
    val outputStream: FileOutputStream = new FileOutputStream(fileName)

    var zipEntry: ZipEntry = null
    var founded: Boolean = false
    while ({
      zipEntry = zipInputStream.getNextEntry
      Option(zipEntry).isDefined && !founded
    }) {

      if (zipEntry.getName.equals(fileName)) {

        val buffer: Array[Byte] = Array.ofDim(9000) // FIXME how to get the dimension of the array
        var length = 0

        while ({
          length = zipInputStream.read(buffer)
          length != -1
        }) {
          outputStream.write(buffer, 0, length)
        }
        outputStream.close()
        founded = true
      }
    }

    zipInputStream.close()
    outputStream /* how can I return the value? */

  }

不写入磁盘中的内容怎么办?

您可以使用 ByteArrayOutputStream 而不是 FileOutputStream 将 zip 条目解压缩到内存中。然后调用toByteArray()就可以了。


另请注意,从技术上讲,如果您可以通过支持其传输的 deflate 编码(通常是Zip 文件中使用的标准压缩)。

所以,基本上我做了与@cbley 推荐的相同的事情。我返回了一个字节数组并定义了 content-type 以便浏览器可以施展魔法!

def getFileContent(fileName: String): IBinaryContent = {
    val content: IBinaryContent = getBinaryContent(...)

    val zipInputStream: ZipInputStream = new ZipInputStream(content.getInputStream)
    val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream()

    var zipEntry: ZipEntry = null
    var founded: Boolean = false
    while ({
      zipEntry = zipInputStream.getNextEntry
      Option(zipEntry).isDefined && !founded
    }) {

      if (zipEntry.getName.equals(fileName)) {

        val buffer: Array[Byte] = Array.ofDim(zipEntry.getSize)
        var length = 0

        while ({
          length = zipInputStream.read(buffer)
          length != -1
        }) {
          outputStream.write(buffer, 0, length)
        }
        outputStream.close()
        founded = true
      }
    }

    zipInputStream.close()
    outputStream.toByteArray

  }

// in my rest service
@GET
@Path("/content/${fileName}")
def content(@PathVariable fileName): Response = {
    val content = getFileContent(fileName)
    Response.ok(content)
        .header("Content-type", new Tika().detect(fileName)) // I'm using Tika but it's possible to use other libraries
        .build()
}