-
Notifications
You must be signed in to change notification settings - Fork 469
Open
Description
Hi,
I asked this question last Feb but didn't get any response. #257
I would like now to share my solution to unzipInMemory and it seems to work just fine, please see if you like to use it or make it better and hopefully integrate it in this nice library.
The only method you need is unzipInMemory to be added to zip.swift file. I have also included a nice URL extension to use along with sample usage.
If you like it feel free to nominate a star :] https://stars.github.com/nominate/
Also please let me know if there are issues with the solution or if you have a better approach. Also if we need to create another method for unzipping multiple files in memory or modify this one.
/**
Unzip data in memory.
- parameter data: The Data object containing the zip file content.
- parameter password: An optional password string for encrypted zip files.
- parameter fileOutputHandler: A closure called for each unzipped file, providing the unzipped data and the file name.
- parameter progress: A progress closure called after unzipping each file in the archive, with a Double value between 0 and 1.
- throws: Error if unzipping fails.
- notes: Supports implicit progress composition.
*/
class func unzipInMemory(_ zipFilePath: URL, password: String?, fileOutputHandler: @escaping (_ unzippedData: Data, _ fileName: String) -> Void) throws {
// File manager
let fileManager = FileManager.default
// Check whether a zip file exists at path.
let path = zipFilePath.path
guard fileManager.fileExists(atPath: path), !fileExtensionIsInvalid(zipFilePath.pathExtension) else {
print("File not found at path: \(path)")
throw ZipError.fileNotFound
}
// Unzip setup
var ret: Int32 = 0
let bufferSize: UInt32 = 4096
var buffer = [CUnsignedChar](repeating: 0, count: Int(bufferSize))
// Begin unzipping
let zip = unzOpen64(path)
defer { unzClose(zip) }
if unzGoToFirstFile(zip) != UNZ_OK {
throw ZipError.unzipFail
}
repeat {
var readBytes: Int32 = 0
// Open the current file
if let cPassword = password?.cString(using: .ascii) {
ret = unzOpenCurrentFilePassword(zip, cPassword)
} else {
ret = unzOpenCurrentFile(zip)
}
guard ret == UNZ_OK else {
throw ZipError.unzipFail
}
var fileInfo = unz_file_info64()
memset(&fileInfo, 0, MemoryLayout<unz_file_info>.size)
ret = unzGetCurrentFileInfo64(zip, &fileInfo, nil, 0, nil, 0, nil, 0)
guard ret == UNZ_OK else {
unzCloseCurrentFile(zip)
throw ZipError.unzipFail
}
let fileNameSize = Int(fileInfo.size_filename) + 1
let fileName = UnsafeMutablePointer<CChar>.allocate(capacity: fileNameSize)
defer { free(fileName) }
unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0)
fileName[Int(fileInfo.size_filename)] = 0
let pathString = String(cString: fileName)
var fileData = Data()
// Read file data
repeat {
readBytes = unzReadCurrentFile(zip, &buffer, bufferSize)
if readBytes > 0 {
fileData.append(buffer, count: Int(readBytes))
}
} while readBytes > 0
// Close the current file
let crcRet = unzCloseCurrentFile(zip)
guard crcRet == UNZ_OK else {
throw ZipError.unzipFail
}
// Call the output handler with the unzipped data
fileOutputHandler(fileData, pathString)
// Move to the next file
ret = unzGoToNextFile(zip)
} while (ret == UNZ_OK)
}extension URL {
/**
Unzip a zip file located at the URL.
- parameter hash: An optional password string for encrypted zip files.
- parameter progress: An optional closure called with the progress of unzipping, where the Double value is between 0 and 1.
- returns: The content of the unzipped file as a String, or nil if unzipping fails.
- throws: An error if unzipping fails.
- notes: Supports sequential processing of unzipped files.
*/
func unzip(hash: String? = nil, progress: ((Double) -> Void)? = nil) -> String? {
var extractedContent: String?
let semaphore = DispatchSemaphore(value: 0)
do {
try Zip.unzipInMemory(self, password: hash) { unzippedData, fileName in
print("Unzipped file: \(fileName), data size: \(unzippedData.count) bytes")
extractedContent = String(decoding: unzippedData, as: UTF8.self)
if let content = extractedContent {
print("Content of \(fileName):\n\(content)")
} else {
print("Unable to convert unzipped data to string for file: \(fileName)")
}
semaphore.signal()
}
semaphore.wait()
return extractedContent
} catch {
print("Error unzipping data: \(error.localizedDescription)")
return nil
}
}
}// MARK: Sample Code - Usage:
struct Constants {
static let hash = "your_password_hash" // Replace with your actual hash
}
// URL of the zip file
let fileURL = URL(fileURLWithPath: "path/to/your/file.zip")
do {
try fileURL.unzip(hash: Constants.hash) { unzippedFile in
print("Unzipped file: \(unzippedFile.lastPathComponent)")
} progress: { progress in
print("Progress: \(progress * 100)%")
}
} catch {
print("An error occurred during unzipping: \(error.localizedDescription)")
}adirkol
Metadata
Metadata
Assignees
Labels
No labels