Skip to content

Instantly share code, notes, and snippets.

@rtfpessoa
Last active September 25, 2015 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rtfpessoa/0c2729538e4836b6436a to your computer and use it in GitHub Desktop.
Save rtfpessoa/0c2729538e4836b6436a to your computer and use it in GitHub Desktop.
Zip Utils Scala Wrapper
import java.util.zip.{ZipEntry, ZipFile, ZipOutputStream}
import better.files._
import scala.collection.JavaConversions.enumerationAsScalaIterator
import scala.language.existentials
import scala.util.{Properties, Try}
object ZipUtils {
def zip(basePath: File): Try[File] = {
val zipFile = File.newTemp(basePath.nameWithoutExtension, ".zip")
val zipEntries = basePath match {
case directory if directory.isDirectory =>
directory.listRecursively()
.filter(_.fullPath != directory.fullPath)
.collect {
case file if file.isDirectory =>
val relativeDirPath = directory.path.relativize(file.path).toString
// make sure it ends with / for ZipEntry to consider it a directory
val cleanRelativeDirPath = relativeDirPath.stripPrefix("/").stripSuffix("/")
val zipRelativeDirPath = s"$cleanRelativeDirPath/"
(file, new ZipEntry(zipRelativeDirPath))
case file =>
(file, new ZipEntry(directory.path.relativize(file.path).toString))
}
case file =>
val relativePath = file.parent.path.relativize(file.path).toString
List((file, new ZipEntry(relativePath)))
}
Try {
val zipOutputStream = new ZipOutputStream(zipFile.out)
zipEntries.foreach {
case (file, entry) if file.isDirectory =>
zipOutputStream.putNextEntry(entry)
zipOutputStream.closeEntry()
case (file, entry) =>
zipOutputStream.putNextEntry(entry)
zipOutputStream.write(file.bytes.toArray)
zipOutputStream.closeEntry()
}
zipOutputStream.close()
zipFile
}
}
def unzip(zipFile: File): Try[File] = {
val outputDir = File.newTempDir(zipFile.nameWithoutExtension)
Try {
val zipArchive = new ZipFile(zipFile.toJava)
val (directories, files) = enumerationAsScalaIterator(zipArchive.entries()).partition(_.isDirectory)
// process directories first to avoid failing file creation
directories.foreach { dir =>
Try {
outputDir / dir.getName mkdirs()
}
}
files.foreach { file =>
Try {
val fileStream = zipArchive.getInputStream(file)
val fileContent = fileStream.lines.mkString(Properties.lineSeparator)
outputDir / file.getName write fileContent
}
}
outputDir
}
}
}
import better.files.File
import org.specs2.mutable._
class ZipUtilsSpec extends Specification {
// Set sequential execution
sequential
case class TestFile(name: String, content: Option[String])
val files = Seq(
TestFile("file1.json", Some( """{"field1":"valueA","field2":"valueB"}""")),
TestFile("file2.json", Some( """{"field1":"valueA","field2":"valueB"}""")),
TestFile("subDir", None),
TestFile("subDir/file3.json", Some( """{"field1":"valueA","field2":"valueB"}""")),
TestFile("emptySubDir", None)
)
"ZipUtils" should {
"zip and unzip" in {
val zipDir = File.newTempDir("zip-files")
files.foreach {
case TestFile(name, Some(content)) =>
zipDir / name write content
case TestFile(name, None) =>
zipDir / name mkdirs()
}
ZipUtils.zip(zipDir).map { zipFile =>
ZipUtils.unzip(zipFile).map { outputDir =>
val extractedFiles = outputDir.listRecursively()
.filter(_.fullPath != outputDir.fullPath)
.collect {
case file if file.isRegularFile =>
val relativeName = outputDir.path.relativize(file.path).toString
val content = file.contentAsString
TestFile(relativeName, Some(content))
case file if file.isDirectory =>
val relativeName = outputDir.path.relativize(file.path).toString
TestFile(relativeName, None)
}.toSeq
extractedFiles must containTheSameElementsAs(files)
}.getOrElse(ko("Failed to unzip"))
}.getOrElse(ko("Failed to zip"))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment