diff --git a/Zip/Zip.swift b/Zip/Zip.swift index b326caad7569a6e7fa00cf8fd2387b516e555553..be47265a5cd26cee8ebb055ceadb9dbe35b063ed 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -48,6 +48,20 @@ public enum ZipCompression: Int { } } +/// Data in memory that will be archived as a file. +public struct ArchiveFile { + var filename:String + var data:NSData + var modifiedTime:Date? + + public init(filename:String, data:NSData, modifiedTime:Date?) { + self.filename = filename + self.data = data + self.modifiedTime = modifiedTime + } +} + + /// Zip class public class Zip { @@ -364,6 +378,102 @@ public class Zip { progressTracker.completedUnitCount = Int64(totalSize) } + + /** + Zip data in memory. + + - parameter archiveFiles:Array of Archive Files. + - parameter zipFilePath: Destination NSURL, should lead to a .zip filepath. + - parameter password: Password string. Optional. + - parameter compression: Compression strategy + - parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1. + + - throws: Error if zipping fails. + + - notes: Supports implicit progress composition + */ + public class func zipData(archiveFiles:[ArchiveFile], zipFilePath:URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws { + + let destinationPath = zipFilePath.path + + // Progress handler set up + var currentPosition: Int = 0 + var totalSize: Int = 0 + + for archiveFile in archiveFiles { + totalSize += archiveFile.data.length + } + + let progressTracker = Progress(totalUnitCount: Int64(totalSize)) + progressTracker.isCancellable = false + progressTracker.isPausable = false + progressTracker.kind = ProgressKind.file + + // Begin Zipping + let zip = zipOpen(destinationPath, APPEND_STATUS_CREATE) + + for archiveFile in archiveFiles { + + // Skip empty data + if archiveFile.data.length == 0 { + continue + } + + // Setup the zip file info + var zipInfo = zip_fileinfo(tmz_date: tm_zip(tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 0, tm_mon: 0, tm_year: 0), + dosDate: 0, + internal_fa: 0, + external_fa: 0) + + if let modifiedTime = archiveFile.modifiedTime { + let calendar = Calendar.current + zipInfo.tmz_date.tm_sec = UInt32(calendar.component(.second, from: modifiedTime)) + zipInfo.tmz_date.tm_min = UInt32(calendar.component(.minute, from: modifiedTime)) + zipInfo.tmz_date.tm_hour = UInt32(calendar.component(.hour, from: modifiedTime)) + zipInfo.tmz_date.tm_mday = UInt32(calendar.component(.day, from: modifiedTime)) + zipInfo.tmz_date.tm_mon = UInt32(calendar.component(.month, from: modifiedTime)) + zipInfo.tmz_date.tm_year = UInt32(calendar.component(.year, from: modifiedTime)) + } + + // Write the data as a file to zip + zipOpenNewFileInZip3(zip, + archiveFile.filename, + &zipInfo, + nil, + 0, + nil, + 0, + nil, + Z_DEFLATED, + compression.minizipCompression, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + password, + 0) + zipWriteInFileInZip(zip, archiveFile.data.bytes, UInt32(archiveFile.data.length)) + zipCloseFileInZip(zip) + + // Update progress handler + currentPosition += archiveFile.data.length + + if let progressHandler = progress{ + progressHandler((Double(currentPosition/totalSize))) + } + + progressTracker.completedUnitCount = Int64(currentPosition) + } + + zipClose(zip, nil) + + // Completed. Update progress handler. + if let progressHandler = progress{ + progressHandler(1.0) + } + + progressTracker.completedUnitCount = Int64(totalSize) + } /** Check if file extension is invalid.