Skip to content

Commit

Permalink
unzip
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone committed May 26, 2023
1 parent 0be6991 commit 4914f4a
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
Expand Up @@ -9,6 +9,7 @@ import zio._
import zio.nio.file._

import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

import org.knora.webapi.util.ZScopedJavaIoStreams
Expand Down Expand Up @@ -70,4 +71,36 @@ object ZipUtility {

private def getZipEntry(entry: Path, srcFolder: Path) = new ZipEntry(srcFolder.relativize(entry).toString())

/**
* Unzips a zip file to the specified destination folder.
*
* @param zipFile The path to the zip file to be unzipped.
* @param destinationFolder The path to the folder where the content of the zip file is written to.
*/
def unzipFile(zipFile: Path, destinationFolder: Path): Task[Path] = ZIO.scoped {
ZScopedJavaIoStreams.zipInputStream(zipFile).flatMap(unzip(_, destinationFolder)).as(destinationFolder)
}
private def unzip(zipInput: ZipInputStream, destinationFolder: Path): Task[Path] =
unzipNextEntry(zipInput, destinationFolder)
// recursively unpack the rest of the stream
.flatMap(entry => unzip(zipInput, destinationFolder).when(entry.isDefined))
.as(destinationFolder)
private def unzipNextEntry(zipInput: ZipInputStream, destinationFolder: Path) = {
val acquire = ZIO.attemptBlocking(Option(zipInput.getNextEntry))
val release = ZIO.attemptBlocking(zipInput.closeEntry()).logError.ignore
val getEntry = ZIO.acquireRelease(acquire)(_ => release)
val createParentIfNotExists = (path: Path) =>
path.parent.map(d => Files.createDirectories(d).whenZIO(Files.notExists(d)).unit).getOrElse(ZIO.unit)
val unzipToFile = (path: Path) =>
ZScopedJavaIoStreams.fileOutputStream(path).flatMap(fos => ZIO.attemptBlocking(zipInput.transferTo(fos)))
ZIO.scoped {
getEntry.tapSome { case Some(entry) =>
val targetPath = destinationFolder / entry.getName
entry match {
case _ if entry.isDirectory => Files.createDirectories(targetPath)
case _ => createParentIfNotExists(targetPath) *> unzipToFile(targetPath)
}
}
}
}
}
Expand Up @@ -10,6 +10,7 @@ import zio._
import java.io._
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

object ZScopedJavaIoStreams {
Expand Down Expand Up @@ -58,6 +59,12 @@ object ZScopedJavaIoStreams {
fileOutputStream(file).flatMap(fos => ZIO.acquireRelease(acquire(fos))(release))
}

def zipInputStream(path: nio.file.Path): ZIO[Scope, Throwable, ZipInputStream] = zipInputStream(path.toFile)
def zipInputStream(file: File): ZIO[Scope, Throwable, ZipInputStream] = {
def acquire(fis: InputStream) = ZIO.attempt(new ZipInputStream(fis))
fileInputStream(file).flatMap(fis => ZIO.acquireRelease(acquire(fis))(release))
}

/**
* Opens or creates a file, returning an output stream that may be used to write bytes to the file.
* Truncates and overwrites an existing file, or create the file if it doesn't initially exist.
Expand Down
@@ -0,0 +1,46 @@
package org.knora.webapi.slice.admin.domain.service

import zio._
import zio.nio.file._
import zio.test._

object ZipUtilitySpec extends ZIOSpecDefault {
private val testFolderName = "test-folder"
private val testFileName = "test-file.txt"
private val testFileContent = List("This is a test file.", "Content is not important.")

private val createTestDirectory = for {
tmp <- Files.createTempDirectoryScoped(Some("tmp"), fileAttributes = Nil).logError
_ <- Files.createDirectory(tmp / testFolderName)
_ <- Files.createFile(tmp / testFolderName / testFileName)
_ <- Files.writeLines(tmp / testFolderName / testFileName, testFileContent)
} yield tmp

private def verifyUnzipped(path: Path) = for {
folderExists <- Files.isDirectory(path / testFolderName)
_ <- ZIO.logError(s"Folder does not exist: ${path / testFolderName}").when(!folderExists)
expectedFile = path / testFolderName / testFileName
fileContentIsCorrect <-
Files.isRegularFile(expectedFile) && Files.readAllLines(expectedFile).map(_ == testFileContent)
_ <- ZIO.logError(s"File content $expectedFile is not correct").when(!fileContentIsCorrect)
} yield folderExists && fileContentIsCorrect

override val spec: Spec[Scope, Throwable] = suite("ZipUtility")(
test("creating the test directory should work") {
for {
tmp <- createTestDirectory
createdCorrectly <- verifyUnzipped(tmp)
} yield assertTrue(createdCorrectly)
},
test("should zip/unzip a folder with a single file") {
for {
zipThis <- createTestDirectory
tmpZipped <- Files.createTempDirectoryScoped(Some("zipped"), fileAttributes = Nil)
zipped <- ZipUtility.zipFolder(zipThis, tmpZipped)
tmpUnzipped <- Files.createTempDirectoryScoped(Some("unzipped"), fileAttributes = Nil)
unzipped <- ZipUtility.unzipFile(zipped, tmpUnzipped)
result <- verifyUnzipped(unzipped)
} yield assertTrue(result)
}
)
}

0 comments on commit 4914f4a

Please sign in to comment.