如何使用 scala 解压缩 zip 文件?

How to unzip a zip file using scala?

基本上我需要解压缩一个 .zip 文件,其中包含一个名为 modeled 的文件夹,该文件夹又包含许多 excel 文件。

我很幸运地找到了已经编写好的代码 (ZipArchive),该代码旨在解压缩 zip 文件,但我不明白为什么在我使用它时它会抛出错误消息。 ZipArchive 的代码和错误信息如下:

import java.io.{OutputStream, InputStream, File, FileOutputStream}
import java.util.zip.{ZipEntry, ZipFile}
import scala.collection.JavaConversions._

object ZipArchive {

  val BUFSIZE = 4096
  val buffer = new Array[Byte](BUFSIZE)

  def unZip(source: String, targetFolder: String) = {
    val zipFile = new ZipFile(source)

    unzipAllFile(zipFile.entries.toList, getZipEntryInputStream(zipFile)_, new File(targetFolder))
  }

  def getZipEntryInputStream(zipFile: ZipFile)(entry: ZipEntry) = zipFile.getInputStream(entry)

  def unzipAllFile(entryList: List[ZipEntry], inputGetter: (ZipEntry) => InputStream, targetFolder: File): Boolean = {

    entryList match {
      case entry :: entries =>

        if (entry.isDirectory)
          new File(targetFolder, entry.getName).mkdirs
        else
          saveFile(inputGetter(entry), new FileOutputStream(new File(targetFolder, entry.getName)))

        unzipAllFile(entries, inputGetter, targetFolder)
      case _ =>
        true
    }
  }

  def saveFile(fis: InputStream, fos: OutputStream) = {
    writeToFile(bufferReader(fis)_, fos)
    fis.close
    fos.close
  }

  def bufferReader(fis: InputStream)(buffer: Array[Byte]) = (fis.read(buffer), buffer)

  def writeToFile(reader: (Array[Byte]) => Tuple2[Int, Array[Byte]], fos: OutputStream): Boolean = {
    val (length, data) = reader(buffer)
    if (length >= 0) {
      fos.write(data, 0, length)
      writeToFile(reader, fos)
    } else
      true
  }
}

错误信息:

java.io.FileNotFoundException: src/test/resources/oepTemp/modeled/EQ_US_2_NULL_('CA')_ALL_ELT_IL_EQ_US.xlsx (No such file or directory), took 6.406 sec
[error]     at java.io.FileOutputStream.open(Native Method)
[error]     at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
[error]     at java.io.FileOutputStream.<init>(FileOutputStream.java:171)
[error]     at com.contract.testing.ZipArchive$.unzipAllFile(ZipArchive.scala:28)
[error]     at com.contract.testing.ZipArchive$.unZip(ZipArchive.scala:15)
[error]     at com.contract.testing.OepStepDefinitions$$anonfun.apply$mcZ$sp(OepStepDefinitions.scala:175)
[error]     at com.contract.testing.OepStepDefinitions$$anonfun.apply(OepStepDefinitions.scala:150)
[error]     at com.contract.testing.OepStepDefinitions$$anonfun.apply(OepStepDefinitions.scala:150)
[error]     at cucumber.api.scala.ScalaDsl$StepBody$$anonfun$apply.applyOrElse(ScalaDsl.scala:61)
[error]     at cucumber.api.scala.ScalaDsl$StepBody$$anonfun$apply.applyOrElse(ScalaDsl.scala:61)
[error]     at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
[error]     at cucumber.runtime.scala.ScalaStepDefinition.execute(ScalaStepDefinition.scala:71)
[error]     at cucumber.runtime.StepDefinitionMatch.runStep(StepDefinitionMatch.java:37)
[error]     at cucumber.runtime.Runtime.runStep(Runtime.java:298)
[error]     at cucumber.runtime.model.StepContainer.runStep(StepContainer.java:44)
[error]     at cucumber.runtime.model.StepContainer.runSteps(StepContainer.java:39)
[error]     at cucumber.runtime.model.CucumberScenario.run(CucumberScenario.java:48)
[error]     at cucumber.runtime.junit.ExecutionUnitRunner.run(ExecutionUnitRunner.java:91)
[error]     at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:63)
[error]     at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:18)
[error]     ...

所以根据错误消息,它似乎在尝试查找导出的 excel 文件?这部分完全让我失望。任何帮助将不胜感激。 我在下面添加了我如何调用该方法,也许我在做一些愚蠢的事情。如果您可以推荐的话,我也准备使用其他方法来提取我的 zip 文件。

val tempDirectoryDir = "src/test/resources/oepTemp/"
ZipArchive.unZip(tempDirectoryDir + "Sub Region Input - Output.zip", tempDirectoryDir)

既然我们正在使用 java 中的一些实用程序,这里是一个基于 this 的版本,翻译成 scala,也许这应该更实用,但它很有用

package zip

import java.io.{ IOException, FileOutputStream, FileInputStream, File }
import java.util.zip.{ ZipEntry, ZipInputStream }

/**
 * Created by anquegi on 04/06/15.
 */
object Unzip extends App {

  val INPUT_ZIP_FILE: String = "src/main/resources/my-zip.zip";
  val OUTPUT_FOLDER: String = "src/main/resources/my-zip";

  def unZipIt(zipFile: String, outputFolder: String): Unit = {

    val buffer = new Array[Byte](1024)

    try {

      //output directory
      val folder = new File(OUTPUT_FOLDER);
      if (!folder.exists()) {
        folder.mkdir();
      }

      //zip file content
      val zis: ZipInputStream = new ZipInputStream(new FileInputStream(zipFile));
      //get the zipped file list entry
      var ze: ZipEntry = zis.getNextEntry();

      while (ze != null) {

        val fileName = ze.getName();
        val newFile = new File(outputFolder + File.separator + fileName);

        System.out.println("file unzip : " + newFile.getAbsoluteFile());

        //create folders
        new File(newFile.getParent()).mkdirs();

        val fos = new FileOutputStream(newFile);

        var len: Int = zis.read(buffer);

        while (len > 0) {

          fos.write(buffer, 0, len)
          len = zis.read(buffer)
        }

        fos.close()
        ze = zis.getNextEntry()
      }

      zis.closeEntry()
      zis.close()

    } catch {
      case e: IOException => println("exception caught: " + e.getMessage)
    }

  }

  Unzip.unZipIt(INPUT_ZIP_FILE, OUTPUT_FOLDER)

}
import java.io.FileInputStream
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import scala.language.reflectiveCalls
import scala.util.Try

import org.apache.commons.io.IOUtils

def using[T <: { def close() }, U](resource: T)(block: T => U): U = {
  try {
    block(resource)
  } finally {
    if (resource != null) {
        resource.close()
    }
  }
}

def processZipFile(zipFile: ZipFile)(doStuff: ZipEntry => Unit) {
  using(new ZipInputStream(new FileInputStream(zipFile))) { zipInputStream =>
    val entries = Stream.continually(Try(zipInputStream.getNextEntry()).getOrElse(null))
        .takeWhile(_ != null) // while not EOF and not corrupted
        .foreach(doStuff)
        .force
  }
}

这是一种更实用、更精确的方法

import java.io.{FileInputStream, FileOutputStream}
import java.util.zip.ZipInputStream
val fis = new FileInputStream("htl.zip")
val zis = new ZipInputStream(fis)
Stream.continually(zis.getNextEntry).takeWhile(_ != null).foreach{ file =>
    val fout = new FileOutputStream(file.getName)
    val buffer = new Array[Byte](1024)
    Stream.continually(zis.read(buffer)).takeWhile(_ != -1).foreach(fout.write(buffer, 0, _))
}

尝试使用 Tian-Liang 的解决方案,我意识到它不适用于具有目录结构的 zip。所以我是这样采用的:

  import java.io.{FileOutputStream, InputStream}
  import java.nio.file.Path
  import java.util.zip.ZipInputStream

  def unzip(zipFile: InputStream, destination: Path): Unit = {
    val zis = new ZipInputStream(zipFile)

    Stream.continually(zis.getNextEntry).takeWhile(_ != null).foreach { file =>
      if (!file.isDirectory) {
        val outPath = destination.resolve(file.getName)
        val outPathParent = outPath.getParent
        if (!outPathParent.toFile.exists()) {
          outPathParent.toFile.mkdirs()
        }

        val outFile = outPath.toFile
        val out = new FileOutputStream(outFile)
        val buffer = new Array[Byte](4096)
        Stream.continually(zis.read(buffer)).takeWhile(_ != -1).foreach(out.write(buffer, 0, _))
      }
    }
  }

来晚了,但我会利用 scala.collection.JavaConverters 在 zip 文件条目上获得 for-loop,并利用 java.nio.Files 获得简单的复制和目录创建:

import java.nio.file.{Files, Path}
import java.util.zip.ZipFile
import scala.collection.JavaConverters._

def unzip(zipPath: Path, outputPath: Path): Unit = {
  val zipFile = new ZipFile(zipPath.toFile)
  for (entry <- zipFile.entries.asScala) {
    val path = outputPath.resolve(entry.getName)
    if (entry.isDirectory) {
      Files.createDirectories(path)
    } else {
      Files.createDirectories(path.getParent)
      Files.copy(zipFile.getInputStream(entry), path)
    }
  }
}

我会将 ZipFile.close() 的自动调用合并到史蒂夫的答案中。它是通过 Noel Yap 的回答中提供的 using 方法制作的。

import java.nio.file.{Files, Path}
import java.util.zip.ZipFile
import scala.collection.JavaConverters._

def using[T <: {def close()}, U](resource: T)(block: T => U): U = {
  try {
    block(resource)
  } finally {
    if (resource != null) {
      resource.close()
    }
  }
}

def unzip(zipPath: Path, outputPath: Path): Unit = {
  using(new ZipFile(zipPath.toFile)) { zipFile =>
    for (entry <- zipFile.entries.asScala) {
      val path = outputPath.resolve(entry.getName)
      if (entry.isDirectory) {
        Files.createDirectories(path)
      } else {
        Files.createDirectories(path.getParent)
        Files.copy(zipFile.getInputStream(entry), path)
      }
    }
  }
}