@@ -10,9 +10,22 @@ import org.apache.commons.compress.compressors.{
10
10
}
11
11
import java .util .zip .Deflater
12
12
import org .apache .commons .compress .utils .IOUtils
13
+ import java .nio .file .{ Paths , Files , FileSystems , FileSystem , StandardCopyOption }
14
+ import java .nio .file .attribute .{ PosixFilePermission , PosixFilePermissions }
15
+ import java .net .URI
16
+ import scala .collection .JavaConverters ._
13
17
18
+ /**
19
+ *
20
+ *
21
+ *
22
+ * @see http://stackoverflow.com/questions/17888365/file-permissions-are-not-being-preserved-while-after-zip
23
+ * @see http://stackoverflow.com/questions/3450250/is-it-possible-to-create-a-script-to-save-and-restore-permissions
24
+ * @see http://stackoverflow.com/questions/1050560/maintain-file-permissions-when-extracting-from-a-zip-file-using-jdk-5-api
25
+ * @see http://docs.oracle.com/javase/7/docs/technotes/guides/io/fsp/zipfilesystemprovider.html
26
+ */
14
27
object ZipHelper {
15
- case class FileMapping (file : File , name : String , unixMode : Option [ Int ] = None )
28
+ case class FileMapping (file : File , name : String )
16
29
17
30
/**
18
31
* Creates a zip file attempting to give files the appropriate unix permissions using Java 6 APIs.
@@ -41,36 +54,22 @@ object ZipHelper {
41
54
}
42
55
43
56
/**
44
- * Creates a zip file attempting to give files the appropriate unix permissions using Java 6 APIs.
57
+ * Creates a zip file attempting to give files the appropriate unix permissions using Java 7 APIs.
58
+ *
45
59
* Note: This is known to have some odd issues on MacOSX whereby executable permissions
46
60
* are not actually discovered, even though the Info-Zip headers exist and work on
47
61
* many variants of linux. Yay Apple.
62
+ *
48
63
* @param sources The files to include in the zip file.
49
64
* @param outputZip The location of the output file.
50
65
*/
51
66
def zip (sources : Traversable [(File , String )], outputZip : File ): Unit = {
52
- val mappings =
53
- for {
54
- (file, name) <- sources.toSeq
55
- // TODO - Figure out if this is good enough....
56
- perm = if (file.isDirectory || file.canExecute) 0755 else 0644
57
- } yield FileMapping (file, name, Some (perm))
67
+ val mappings = sources.toSeq.map {
68
+ case (file, name) => FileMapping (file, name)
69
+ }
58
70
archive(mappings, outputZip)
59
71
}
60
72
61
- /**
62
- * Creates a zip file using the given set of filters
63
- * @param sources The files to include in the zip file. A File, Location, Permission pairing.
64
- * @param outputZip The location of the output file.
65
- */
66
- def zipWithPerms (sources : Traversable [(File , String , Int )], outputZip : File ): Unit = {
67
- val mappings =
68
- for {
69
- (file, name, perm) <- sources
70
- } yield FileMapping (file, name, Some (perm))
71
- archive(mappings.toSeq, outputZip)
72
- }
73
-
74
73
/**
75
74
* Replaces windows backslash file separator with a forward slash, this ensures the zip file entry is correct for
76
75
* any system it is extracted on.
@@ -84,31 +83,41 @@ object ZipHelper {
84
83
path.replace(sep, '/' )
85
84
}
86
85
86
+ /**
87
+ *
88
+ */
87
89
private def archive (sources : Seq [FileMapping ], outputFile : File ): Unit = {
88
- if (outputFile.isDirectory) sys.error(" Specified output file " + outputFile + " is a directory." )
89
- else {
90
- val outputDir = outputFile.getParentFile
91
- IO createDirectory outputDir
92
- withZipOutput(outputFile) { output =>
93
- for (FileMapping (file, name, mode) <- sources; if ! file.isDirectory) {
94
- val entry = new ZipArchiveEntry (file, normalizePath(name))
95
- // Now check to see if we have permissions for this sucker.
96
- mode foreach (entry.setUnixMode)
97
- output putArchiveEntry entry
98
- // TODO - Write file into output?
99
- IOUtils .copy(new java.io.FileInputStream (file), output)
100
- output.closeArchiveEntry()
101
- }
90
+ require(! outputFile.isDirectory, " Specified output file " + outputFile + " is a directory." )
91
+
92
+ // make sure everything is available
93
+ val outputDir = outputFile.getParentFile
94
+ IO createDirectory outputDir
95
+
96
+ // zipping the sources into the output zip
97
+ withZipFilesystem(outputFile) { system =>
98
+ sources foreach {
99
+ case FileMapping (dir, name) if dir.isDirectory => Files createDirectories (system getPath name)
100
+ case FileMapping (file, name) => Files copy (file.toPath, system getPath name, StandardCopyOption .COPY_ATTRIBUTES )
102
101
}
103
102
}
103
+
104
104
}
105
105
106
- private def withZipOutput (file : File )(f : ZipArchiveOutputStream => Unit ): Unit = {
107
- val zipOut = new ZipArchiveOutputStream (file)
108
- zipOut setLevel Deflater .BEST_COMPRESSION
109
- try { f(zipOut) }
110
- finally {
111
- zipOut.close()
106
+ /**
107
+ * Opens a zip filesystem and creates the file if neccessary
108
+ *
109
+ * @param zipFile
110
+ * @param f: FileSystem => Unit, logic working in the filesystem
111
+ */
112
+ def withZipFilesystem (zipFile : File )(f : FileSystem => Unit ) {
113
+ val env = Map (" create" -> " true" ).asJava
114
+ val uri = URI .create(" jar:file:" + zipFile.getAbsolutePath)
115
+
116
+ val system = FileSystems .newFileSystem(uri, env)
117
+ try {
118
+ f(system)
119
+ } finally {
120
+ system.close()
112
121
}
113
122
}
114
123
}
0 commit comments