【问题标题】:How to unzip a zip file using scala?如何使用 scala 解压缩 zip 文件?
【发布时间】:2023-03-21 09:36:01
【问题描述】:

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

我很幸运地找到了已经编写好的用于解压缩 zip 文件的代码 (ZipArchive),但我无法弄清楚为什么在使用它时会抛出错误消息。 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$1.apply$mcZ$sp(OepStepDefinitions.scala:175)
[error]     at com.contract.testing.OepStepDefinitions$$anonfun$1.apply(OepStepDefinitions.scala:150)
[error]     at com.contract.testing.OepStepDefinitions$$anonfun$1.apply(OepStepDefinitions.scala:150)
[error]     at cucumber.api.scala.ScalaDsl$StepBody$$anonfun$apply$1.applyOrElse(ScalaDsl.scala:61)
[error]     at cucumber.api.scala.ScalaDsl$StepBody$$anonfun$apply$1.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)

【问题讨论】:

    标签: excel scala unzip


    【解决方案1】:

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

    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, _))
    }
    

    【讨论】:

      【解决方案2】:

      尝试使用 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, _))
            }
          }
        }
      

      【讨论】:

      • 只是好奇,不关闭FileOutputStream对象可以吗?
      • 几年后我会认为你是对的。关闭任何输出流总是更安全。 :)
      【解决方案3】:

      既然我们使用了一些来自 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)
      
      }
      

      【讨论】:

        【解决方案4】:

        在这里玩游戏已经很晚了,但我会利用scala.collection.JavaConverters 对 zip 文件条目进行 for 循环,并利用 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)
            }
          }
        }
        

        【讨论】:

          【解决方案5】:
          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
            }
          }
          

          【讨论】:

          • Try 是一个不错的选择;所以这对我很有用。然而,map 将提供以下 force,其中贪婪的 foreach 没有给 force 工作留下任何东西。
          • 为其他读者记录:如果流中嵌入了“坏”文件,Try 允许继续处理。不确定这是否是一个真正的问题;仍然 - 这提供了一些方法来管理错误情况,而不会丢弃整个流。
          【解决方案6】:

          我会将 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)
                }
              }
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-08
            • 1970-01-01
            • 1970-01-01
            • 2018-06-19
            • 2016-10-15
            相关资源
            最近更新 更多