如何使用Scala将多个文件存档到一个.zip文件中?

19

有人可以发布一个简单的片段来实现这个吗?

文件是文本文件,因此压缩而不仅仅是归档文件会更好。

我将文件名存储在可迭代对象中。

6个回答

28

目前标准的Scala库中没有方法来完成这种操作,不过可以很容易地使用java.util.zip

def zip(out: String, files: Iterable[String]) = {
  import java.io.{ BufferedInputStream, FileInputStream, FileOutputStream }
  import java.util.zip.{ ZipEntry, ZipOutputStream }

  val zip = new ZipOutputStream(new FileOutputStream(out))

  files.foreach { name =>
    zip.putNextEntry(new ZipEntry(name))
    val in = new BufferedInputStream(new FileInputStream(name))
    var b = in.read()
    while (b > -1) {
      zip.write(b)
      b = in.read()
    }
    in.close()
    zip.closeEntry()
  }
  zip.close()
}

我在这里专注于简单性而非效率(没有错误检查,逐字节读写也不是理想的方法),但它能够工作,并且可以非常容易地改进。


我知道这不是最优化的,但没有必要将字节包装在数组中。你可以简单地使用 zip.write(b) - leedm777
1
另外,不要忘记在 finally 块中关闭 inzip - yby
sbt 中的 sbt.IO.zip() - Andrew Scott Evans
1
while循环的替代方案 Stream.continually(in.read()).takeWhile(_ > -1).foreach { b => zip.write(b) } - Majid Hosseini

11

我最近也需要使用zip文件,并发现这个非常好用的实用工具:https://github.com/zeroturnaround/zt-zip

这是压缩目录中所有文件的示例:

import org.zeroturnaround.zip.ZipUtil
ZipUtil.pack(new File("/tmp/demo"), new File("/tmp/demo.zip"))

非常方便。


6
这是一种更加Scala风格的写法,如果您喜欢函数式编程:
  def compress(zipFilepath: String, files: List[File]) {
    def readByte(bufferedReader: BufferedReader): Stream[Int] = {
      bufferedReader.read() #:: readByte(bufferedReader)
    }
    val zip = new ZipOutputStream(new FileOutputStream(zipFilepath))
    try {
      for (file <- files) {
        //add zip entry to output stream
        zip.putNextEntry(new ZipEntry(file.getName))

        val in = Source.fromFile(file.getCanonicalPath).bufferedReader()
        try {
          readByte(in).takeWhile(_ > -1).toList.foreach(zip.write(_))
        }
        finally {
          in.close()
        }

        zip.closeEntry()
      }
    }
    finally {
      zip.close()
    }
  }

不要忘记导入:

import java.io.{BufferedReader, FileOutputStream, File}
import java.util.zip.{ZipEntry, ZipOutputStream}
import io.Source

1
readByte(in).takeWhile(_ > -1).toList 在读取大文件时会消耗大量内存。使用 Iterator 可能更好。 - jilen
1
def readByte(bufferedReader: BufferedReader) = Stream.continually(bufferedReader.read()) - nafg

3
特拉维斯(Travis)的回答是正确的,但我稍加修改,以获得他代码更快的版本:
val Buffer = 2 * 1024

def zip(out: String, files: Iterable[String], retainPathInfo: Boolean = true) = {
  var data = new Array[Byte](Buffer)
  val zip = new ZipOutputStream(new FileOutputStream(out))
  files.foreach { name =>
    if (!retainPathInfo)
      zip.putNextEntry(new ZipEntry(name.splitAt(name.lastIndexOf(File.separatorChar) + 1)._2))
    else
      zip.putNextEntry(new ZipEntry(name))
    val in = new BufferedInputStream(new FileInputStream(name), Buffer)
    var b = in.read(data, 0, Buffer)
    while (b != -1) {
      zip.write(data, 0, b)
      b = in.read(data, 0, Buffer)
    }
    in.close()
    zip.closeEntry()
  }
  zip.close()
}

3

使用NIO2的稍微修改过的(更短的)版本:

private def zip(out: Path, files: Iterable[Path]) = {
  val zip = new ZipOutputStream(Files.newOutputStream(out))

  files.foreach { file =>
    zip.putNextEntry(new ZipEntry(file.toString))
    Files.copy(file, zip)
    zip.closeEntry()
  }
  zip.close()
}

1

根据Gabriele Petronella的建议,除此之外,您还需要在pom.xml中添加以下Maven依赖项,并导入以下内容:

import org.zeroturnaround.zip.ZipUtil
import java.io.File

<dependency>
    <groupId>org.zeroturnaround</groupId>
    <artifactId>zt-zip</artifactId>
    <version>1.13</version>
    <type>jar</type>
</dependency>*

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接