Skip to content

Commit

Permalink
Change I/F
Browse files Browse the repository at this point in the history
  • Loading branch information
hossshy committed Jul 28, 2018
1 parent 36b801b commit 33fd75e
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 164 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ project/plugins/project/
.cache
.lib/
.idea/
.DS_Store
.DS_Store
out.zip
34 changes: 14 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,19 @@ Simple zip archive (files compresses recursively) library for Scala.
Using Java NIO.

```scala
// target path
val zip = ScZip("./project")
// Can get file list
zip.dryRun().foreach(println)
// Pass an output file path and run.
zip.zipToFile("./out.zip")
```

### Set exclude condition.

Can set exclude pattern by glob without "glob:".

https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob

```scala
val zip = ScZip("./project", "**/*.{class,cache}")

// Can get byte array instead of saving a zip file.
val bytes: Array[Byte] = zip.zipToBytes()
// Add files recursively and make zip data.
ScZip.zipTreeToFile(Paths.get("./project"), Paths.get("out.zip"))
.foreach(println) // print entry files

// Can get zip data instead of file
val data = ScZip.zipTreeToBytes(Paths.get("./project"))
println(data.length)

// Set exclude condition by glob pattern without "glob:".
ScZip.zipTreeToFile(Paths.get("./project"), Paths.get("out.zip"), ScZip.makeExclude("**/*.{cache,class}"))
.foreach(println)
val data2 = ScZip.zipTreeToBytes(Paths.get("./project"), ScZip.makeExclude("**/*.{cache,class}"))
println(data2.length)
```

That's all.
Expand All @@ -38,7 +32,7 @@ Please append it in your libraryDependencies :)

```scala
libraryDependencies ++= Seq(
"com.yuchesc" %% "sczip" % "0.9.1"
"com.yuchesc" %% "sczip" % "0.9.5"
)
```

Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name := "sczip"

version := "0.9.1"
version := "0.9.5"

organization := "com.yuchesc"

Expand Down
148 changes: 80 additions & 68 deletions src/main/scala/com/yuchesc/sczip/ScZip.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,106 +2,118 @@ package com.yuchesc.sczip

import java.io._
import java.nio.file._
import java.util.zip.ZipOutputStream

import com.yuchesc.sczip.lib._

/**
* Zip executor.
*
* @param targetPath Target zip root path.
* @param exclude Exclude files from target via glob match pattern.
* @param normalizeRootPath If true, eliminate root or relative path from starting point.
* Make zip easily.
*/
class ScZip(targetPath: Path,
exclude: Option[Condition] = None,
normalizeRootPath: Boolean = true) {
object ScZip {

/**
* Walk target path and collect each file path.
* Make exclude object covered with Option.
*
* @return Zip target file list.
* @param pattern glob match pattern
* @return exclude object covered with Option
*/
def dryRun(): Seq[String] = {
val visitor = new ListFileVisitor(exclude, normalizeRootPath)
Files.walkFileTree(targetPath, visitor)
visitor.getResult
}
def makeExclude(pattern: String): Option[Condition] = Option(new PathMatchCondition(pattern))

/**
* Zip files into out stream.
* Add files recursively and make zip data.
*
* @param out stream to write zip data.
* @param targetPath target zip root path
* @param exclude exclude object
* @return zip data
*/
def zipToOutputStream(out: OutputStream): Unit = {
val zip = new ZipOutputStream(out)
Files.walkFileTree(targetPath, new ZipFileVisitor(zip, exclude, normalizeRootPath))
zip.close()
}


def zipToFile(outPathName: String): Unit = zipToFile(Paths.get(outPathName))

def zipToFile(outPath: Path): Unit = {
zipToOutputStream(new FileOutputStream(outPath.toFile))
}

def zipToBytes(): Array[Byte] = {
def zipTreeToBytes(targetPath: Path, exclude: Option[Condition] = None): Array[Byte] = {
val out = new ByteArrayOutputStream()
zipToOutputStream(out)
zipTree(targetPath, out, exclude)
out.toByteArray
}
}

/**
* ScZip object creator.
*/
object ScZip {

/**
* The simplest way to create an object.
* Add files recursively and make and write zip to file.
*
* @param targetPathName Target zip root path string.
* @return object
* @param targetPath target zip root path
* @param outPath output path
* @param exclude exclude object
* @return entry name list
*/
def apply(targetPathName: String): ScZip = apply(Paths.get(targetPathName))
def zipTreeToFile(targetPath: Path, outPath: Path, exclude: Option[Condition] = None): Seq[String] =
zipTree(targetPath, new BufferedOutputStream(new FileOutputStream(outPath.toFile)), exclude)

/**
* Make sczip.
* Add files recursively and make and write zip to out.
*
* @param targetPathName Target zip root path string.
* @param excludePattern Exclude glob match pattern.
* @return object
* @param targetPath Target zip root path
* @param out output stream to write zip data
* @param exclude exclude object
* @return entry name list
*/
def apply(targetPathName: String, excludePattern: String): ScZip = apply(Paths.get(targetPathName), Exclude(excludePattern))
def zipTree(targetPath: Path, out: OutputStream, exclude: Option[Condition] = None): Seq[String] = {
var zip: Option[Zipper] = None
try {
zip = Option(new Zipper(out))
zip.get.addTree(targetPath, exclude)
} finally {
zip.foreach(_.close())
}
}


/**
* Make sczip.
* Make zip data.
*
* @param targetPath Target zip root path.
* @return object
* @param files zip target files
* @return zip data
*/
def apply(targetPath: Path): ScZip = new ScZip(targetPath, None)
def zipFilesToBytes(files: Seq[Path]): Array[Byte] = {
val out = new ByteArrayOutputStream()
zipFiles(files, out)
out.toByteArray
}

/**
* Make sczip.
* Make and write zip to file.
*
* @param targetPath Target zip root path.
* @param exclude Exclude files from target via glob match pattern.
* @param files zip target files
* @param outPath output path
* @return entry name list
*/
def apply(targetPath: Path, exclude: Condition): ScZip = new ScZip(targetPath, Option(exclude))
def zipFilesToFile(files: Seq[Path], outPath: Path): Seq[String] = {
zipFiles(files, new BufferedOutputStream(new FileOutputStream(outPath.toFile)))
}

/**
* Make and write zip to out.
*
* @param files zip target files
* @param out output stream to write zip data
* @return entry name list
*/
def zipFiles(files: Seq[Path], out: OutputStream): Seq[String] = {
var zip: Option[Zipper] = None
try {
zip = Option(new Zipper(out))
files.map(zip.get.add)
} finally {
zip.foreach(_.close())
}
}

def main(args: Array[String]): Unit = {
val zip = ScZip("./project", "**/*.{class,cache}")

val bytes = zip.zipToBytes()
println(bytes.length)

zip.zipToFile("./out.zip")

//zip.dryRun().foreach(println)
val zip2 = new ScZip(targetPath = Paths.get("./src/test/resource"),
exclude = Option(Exclude("**/{test.a,test1.b}")),
normalizeRootPath = false)
zip2.dryRun().foreach(println)
// Add files recursively and make zip data.
ScZip.zipTreeToFile(Paths.get("./project"), Paths.get("out.zip"))
.foreach(println) // print entry files

// Can get zip data instead of file
val data = ScZip.zipTreeToBytes(Paths.get("./project"))
println(data.length)

// Set exclude condition by glob pattern without "glob:".
ScZip.zipTreeToFile(Paths.get("./project"), Paths.get("out.zip"), ScZip.makeExclude("**/*.{cache,class}"))
.foreach(println)
val data2 = ScZip.zipTreeToBytes(Paths.get("./project"), ScZip.makeExclude("**/*.{cache,class}"))
println(data2.length)
}
}
54 changes: 0 additions & 54 deletions src/main/scala/com/yuchesc/sczip/ZipFileVisitor.scala

This file was deleted.

80 changes: 80 additions & 0 deletions src/main/scala/com/yuchesc/sczip/Zipper.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.yuchesc.sczip

import java.io.{BufferedInputStream, OutputStream}
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.{FileVisitResult, Files, Path, SimpleFileVisitor}
import java.util.zip.{ZipEntry, ZipOutputStream}

import com.yuchesc.sczip.lib.Condition

import scala.collection.mutable.ListBuffer

/**
* Zip executor.
*
* @param out the output stream
* @param normalizeRootPath if true, eliminate root or relative path from starting point. (Default: true)
* @param capacity file reading buffer size. (Default: 4096)
*/
class Zipper(out: OutputStream,
val normalizeRootPath: Boolean = true,
val capacity: Int = 4096) {

private val zip: ZipOutputStream = new ZipOutputStream(out)
private val buffer: Array[Byte] = Array.ofDim[Byte](capacity)

protected def getEntryName(file: Path): String = if (normalizeRootPath) {
file.toString dropWhile ("./\\" contains _)
} else {
file.toString
}

/**
* Add a file to zip.
*
* @param path Target file
* @return added entry name
*/
def add(path: Path): String = {
val entryName = getEntryName(path)
val entry = new ZipEntry(entryName)
entry.setTime(Files.getLastModifiedTime(path).toMillis)
zip.putNextEntry(entry)
var in = None: Option[BufferedInputStream]
try {
in = Option(new BufferedInputStream(Files.newInputStream(path)))
Stream.continually(in.get.read(buffer))
.takeWhile(_ != -1)
.foreach(zip.write(buffer, 0, _))
zip.closeEntry()
entryName
} finally {
in.foreach(_.close())
}
}

/**
* Add a file to zip.
*
* @param root Target file path
* @param exclude Exclude files from target via glob match pattern.
* @return added entry name list
*/
def addTree(root: Path, exclude: Option[Condition] = None): Seq[String] = {
val list = ListBuffer.empty[String]
Files.walkFileTree(root, new SimpleFileVisitor[Path] {
override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
if (exclude.isEmpty || !exclude.get.hit(file)) {
list.append(add(file))
}
FileVisitResult.CONTINUE
}
})
list
}

/**
* Close zip stream.
*/
def close(): Unit = zip.close()
}

0 comments on commit 33fd75e

Please sign in to comment.