如何解压缩包含一个文件的大 zip 文件并使用 swift 获取以字节为单位的进度?
How to unzip a big zip file containing one file and get the progress in bytes with swift?
我尝试解压一个仅包含一项(超过 100MB)的大 zip 文件,并希望在解压过程中显示进度。
我找到了可以根据解压缩文件的数量来确定进度的解决方案,但在我的例子中,我只有一个大文件。所以我猜它必须由解压缩的字节数来确定?
实际上我正在使用 SSZipArchive 和以下代码,它工作正常:
var myZipFile:NSString="/Users/user/Library/Developer/CoreSimulator/Devices/mydevice/ziptest/testzip.zip";
var DestPath:NSString="/Users/user/Library/Developer/CoreSimulator/Devices/mydevice/ziptest/";
let unZipped = SSZipArchive.unzipFileAtPath(myZipFile as! String, toDestination: DestPath as! String);
我找不到解决方案。
有人有提示、样本或 link 样本吗?
更新:
下面的代码看起来会按预期工作,但是当只有一个文件被解压缩时,处理程序将只被调用一次(在解压缩结束时):
func unzipFile(sZipFile: String, toDest: String){
SSZipArchive.unzipFileAtPath(sZipFile, toDestination: toDest, progressHandler: {
(entry, zipInfo, readByte, totalByte) -> Void in
println("readByte : \(readByte)") // <- This will be only called once, at the end of unzipping. My 500MB Zipfile holds only one file.
println("totalByte : \(totalByte)")
//Asynchrone task
dispatch_async(dispatch_get_main_queue()) {
println("readByte : \(readByte)")
println("totalByte : \(totalByte)")
//Change progress value
}
}, completionHandler: { (path, success, error) -> Void in
if success {
//SUCCESSFUL!!
} else {
println(error)
}
})
}
更新 2:
正如 "Martin R" 在 SSArchive 中分析的那样,这是不可能的。
有没有其他方法可以解压缩文件并显示基于千字节的进度?
更新 3:
我在"roop"解释解决方案后更改了SSZipArchive.m如下。可能其他人也可以使用它:
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
while (fp) {
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes > 0) {
fwrite(buffer, readBytes, 1, fp );
totalbytesread=totalbytesread+4096;
// Added by me
if (progressHandler)
{
progressHandler(strPath, fileInfo, currentFileNumber, totalbytesread);
}
// End added by me
} else {
break;
}
}
您可以试试这个代码:
SSZipArchive.unzipFileAtPath(filePath, toDestination: self.destinationPath, progressHandler: {
(entry, zipInfo, readByte, totalByte) -> Void in
//Create UIProgressView
//Its an exemple, you can create it with the storyboard...
var progressBar : UIProgressView?
progressBar = UIProgressView(progressViewStyle: .Bar)
progressBar?.center = view.center
progressBar?.frame = self.view.center
progressBar?.progress = 0.0
progressBar?.trackTintColor = UIColor.lightGrayColor();
progressBar?.tintColor = UIColor.redColor();
self.view.addSubview(progressBar)
//Asynchrone task
dispatch_async(dispatch_get_main_queue()) {
println("readByte : \(readByte)")
println("totalByte : \(totalByte)")
//Change progress value
progressBar?.setProgress(Float(readByte/totalByte), animated: true)
//If progressView == 100% then hide it
if readByte == totalByte {
progressBar?.hidden = true
}
}
}, completionHandler: { (path, success, error) -> Void in
if success {
//SUCCESSFUL!!
} else {
println(error)
}
})
希望对你有所帮助!
看
要实现你想要的,你将不得不修改 SSZipArchive 的内部代码。
SSZipArchive uses minizip to provide the zipping functionality. You can see the minizip unzipping API here: unzip.h.
在SSZipArchive.m中,您可以从fileInfo
variable.
中获取正在解压的文件的未压缩大小
可以看到正在读取解压后的内容here:
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
while (fp) {
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes > 0) {
fwrite(buffer, readBytes, 1, fp );
} else {
break;
}
}
您将需要 readBytes
和未压缩的文件大小来计算单个文件的进度。您可以向 SSZipArchive 添加一个新委托,以将这些数据发送回调用代码。
据我所知,最明显的答案是修改 SSZipArchive 的内部代码。但我决定采用不同的方式编写此扩展。理解起来相当简单,但请不要犹豫,提出任何问题。
此外,如果您认为我的解决方案有缺陷或者您知道如何改进它,我将很高兴听到。
这是一个解决方案:
import Foundation
import SSZipArchive
typealias ZippingProgressClosure = (_ zipBytes: Int64, _ totalBytes: Int64) -> ()
private typealias ZipInfo = (contentSize: Int64, zipPath: String, progressHandler: ZippingProgressClosure)
extension SSZipArchive
{
static func createZipFile(atPath destinationPath: String,
withContentsOfDirectory contentPath: String,
keepParentDirectory: Bool,
withPassword password: String? = nil,
byteProgressHandler: @escaping ZippingProgressClosure,
completionHandler: @escaping ClosureWithSuccess)
{
DispatchQueue.global(qos: .background).async {
var timer: Timer? = nil
DispatchQueue.main.async {
//that's a custom function for folder's size calculation
let contentSize = FileManager.default.sizeOfFolder(contentPath)
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(progressUpdate(_:)),
userInfo: ZipInfo(contentSize: contentSize,
zipPath: destinationPath,
progressHandler: byteProgressHandler),
repeats: true)
}
let isSuccess = SSZipArchive.createZipFile(atPath: destinationPath,
withContentsOfDirectory: contentPath,
keepParentDirectory: keepParentDirectory,
withPassword: password,
andProgressHandler: nil)
DispatchQueue.main.async {
timer?.invalidate()
timer = nil
completionHandler(isSuccess)
}
}
}
@objc private static func progressUpdate(_ sender: Timer)
{
guard let info = sender.userInfo as? ZipInfo,
FileManager.default.fileExists(atPath: info.zipPath),
let zipBytesObj = try? FileManager.default.attributesOfItem(atPath: info.zipPath)[FileAttributeKey.size],
let zipBytes = zipBytesObj as? Int64 else {
return
}
info.progressHandler(zipBytes, info.contentSize)
}
}
方法就是这样使用的:
SSZipArchive.createZipFile(atPath: destinationUrl.path,
withContentsOfDirectory: fileUrl.path,
keepParentDirectory: true,
byteProgressHandler: { (zipped, expected) in
//here's the progress code
}) { (isSuccess) in
//here's completion code
}
优点:您不需要修改将被 pods 更新覆盖的内部代码
缺点:如您所见,我正在以 0.1 秒的间隔更新文件大小信息。我不知道获取文件元数据是否会导致性能过载,我找不到任何相关信息。
无论如何,我希望我能帮助到别人:)
SSZipArchive六年未更新,你需要一个新的选择
Zip: Swift 压缩和解压缩文件的框架。
let filePath = Bundle.main.url(forResource: "file", withExtension: "zip")!
let documentsDirectory = FileManager.default.urls(for:.documentDirectory, in: .userDomainMask)[0]
try Zip.unzipFile(filePath, destination: documentsDirectory, overwrite: true, password: "password", progress: { (progress) -> () in
print(progress)
}) // Unzip
let zipFilePath = documentsFolder.appendingPathComponent("archive.zip")
try Zip.zipFiles([filePath], zipFilePath: zipFilePath, password: "password", progress: { (progress) -> () in
print(progress)
}) //Zip
我尝试解压一个仅包含一项(超过 100MB)的大 zip 文件,并希望在解压过程中显示进度。
我找到了可以根据解压缩文件的数量来确定进度的解决方案,但在我的例子中,我只有一个大文件。所以我猜它必须由解压缩的字节数来确定?
实际上我正在使用 SSZipArchive 和以下代码,它工作正常:
var myZipFile:NSString="/Users/user/Library/Developer/CoreSimulator/Devices/mydevice/ziptest/testzip.zip";
var DestPath:NSString="/Users/user/Library/Developer/CoreSimulator/Devices/mydevice/ziptest/";
let unZipped = SSZipArchive.unzipFileAtPath(myZipFile as! String, toDestination: DestPath as! String);
我找不到解决方案。
有人有提示、样本或 link 样本吗?
更新: 下面的代码看起来会按预期工作,但是当只有一个文件被解压缩时,处理程序将只被调用一次(在解压缩结束时):
func unzipFile(sZipFile: String, toDest: String){
SSZipArchive.unzipFileAtPath(sZipFile, toDestination: toDest, progressHandler: {
(entry, zipInfo, readByte, totalByte) -> Void in
println("readByte : \(readByte)") // <- This will be only called once, at the end of unzipping. My 500MB Zipfile holds only one file.
println("totalByte : \(totalByte)")
//Asynchrone task
dispatch_async(dispatch_get_main_queue()) {
println("readByte : \(readByte)")
println("totalByte : \(totalByte)")
//Change progress value
}
}, completionHandler: { (path, success, error) -> Void in
if success {
//SUCCESSFUL!!
} else {
println(error)
}
})
}
更新 2:
正如 "Martin R" 在 SSArchive 中分析的那样,这是不可能的。 有没有其他方法可以解压缩文件并显示基于千字节的进度?
更新 3:
我在"roop"解释解决方案后更改了SSZipArchive.m如下。可能其他人也可以使用它:
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
while (fp) {
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes > 0) {
fwrite(buffer, readBytes, 1, fp );
totalbytesread=totalbytesread+4096;
// Added by me
if (progressHandler)
{
progressHandler(strPath, fileInfo, currentFileNumber, totalbytesread);
}
// End added by me
} else {
break;
}
}
您可以试试这个代码:
SSZipArchive.unzipFileAtPath(filePath, toDestination: self.destinationPath, progressHandler: {
(entry, zipInfo, readByte, totalByte) -> Void in
//Create UIProgressView
//Its an exemple, you can create it with the storyboard...
var progressBar : UIProgressView?
progressBar = UIProgressView(progressViewStyle: .Bar)
progressBar?.center = view.center
progressBar?.frame = self.view.center
progressBar?.progress = 0.0
progressBar?.trackTintColor = UIColor.lightGrayColor();
progressBar?.tintColor = UIColor.redColor();
self.view.addSubview(progressBar)
//Asynchrone task
dispatch_async(dispatch_get_main_queue()) {
println("readByte : \(readByte)")
println("totalByte : \(totalByte)")
//Change progress value
progressBar?.setProgress(Float(readByte/totalByte), animated: true)
//If progressView == 100% then hide it
if readByte == totalByte {
progressBar?.hidden = true
}
}
}, completionHandler: { (path, success, error) -> Void in
if success {
//SUCCESSFUL!!
} else {
println(error)
}
})
希望对你有所帮助!
看
要实现你想要的,你将不得不修改 SSZipArchive 的内部代码。
SSZipArchive uses minizip to provide the zipping functionality. You can see the minizip unzipping API here: unzip.h.
在SSZipArchive.m中,您可以从fileInfo
variable.
可以看到正在读取解压后的内容here:
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
while (fp) {
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes > 0) {
fwrite(buffer, readBytes, 1, fp );
} else {
break;
}
}
您将需要 readBytes
和未压缩的文件大小来计算单个文件的进度。您可以向 SSZipArchive 添加一个新委托,以将这些数据发送回调用代码。
据我所知,最明显的答案是修改 SSZipArchive 的内部代码。但我决定采用不同的方式编写此扩展。理解起来相当简单,但请不要犹豫,提出任何问题。
此外,如果您认为我的解决方案有缺陷或者您知道如何改进它,我将很高兴听到。
这是一个解决方案:
import Foundation
import SSZipArchive
typealias ZippingProgressClosure = (_ zipBytes: Int64, _ totalBytes: Int64) -> ()
private typealias ZipInfo = (contentSize: Int64, zipPath: String, progressHandler: ZippingProgressClosure)
extension SSZipArchive
{
static func createZipFile(atPath destinationPath: String,
withContentsOfDirectory contentPath: String,
keepParentDirectory: Bool,
withPassword password: String? = nil,
byteProgressHandler: @escaping ZippingProgressClosure,
completionHandler: @escaping ClosureWithSuccess)
{
DispatchQueue.global(qos: .background).async {
var timer: Timer? = nil
DispatchQueue.main.async {
//that's a custom function for folder's size calculation
let contentSize = FileManager.default.sizeOfFolder(contentPath)
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(progressUpdate(_:)),
userInfo: ZipInfo(contentSize: contentSize,
zipPath: destinationPath,
progressHandler: byteProgressHandler),
repeats: true)
}
let isSuccess = SSZipArchive.createZipFile(atPath: destinationPath,
withContentsOfDirectory: contentPath,
keepParentDirectory: keepParentDirectory,
withPassword: password,
andProgressHandler: nil)
DispatchQueue.main.async {
timer?.invalidate()
timer = nil
completionHandler(isSuccess)
}
}
}
@objc private static func progressUpdate(_ sender: Timer)
{
guard let info = sender.userInfo as? ZipInfo,
FileManager.default.fileExists(atPath: info.zipPath),
let zipBytesObj = try? FileManager.default.attributesOfItem(atPath: info.zipPath)[FileAttributeKey.size],
let zipBytes = zipBytesObj as? Int64 else {
return
}
info.progressHandler(zipBytes, info.contentSize)
}
}
方法就是这样使用的:
SSZipArchive.createZipFile(atPath: destinationUrl.path,
withContentsOfDirectory: fileUrl.path,
keepParentDirectory: true,
byteProgressHandler: { (zipped, expected) in
//here's the progress code
}) { (isSuccess) in
//here's completion code
}
优点:您不需要修改将被 pods 更新覆盖的内部代码
缺点:如您所见,我正在以 0.1 秒的间隔更新文件大小信息。我不知道获取文件元数据是否会导致性能过载,我找不到任何相关信息。
无论如何,我希望我能帮助到别人:)
SSZipArchive六年未更新,你需要一个新的选择
Zip: Swift 压缩和解压缩文件的框架。
let filePath = Bundle.main.url(forResource: "file", withExtension: "zip")!
let documentsDirectory = FileManager.default.urls(for:.documentDirectory, in: .userDomainMask)[0]
try Zip.unzipFile(filePath, destination: documentsDirectory, overwrite: true, password: "password", progress: { (progress) -> () in
print(progress)
}) // Unzip
let zipFilePath = documentsFolder.appendingPathComponent("archive.zip")
try Zip.zipFiles([filePath], zipFilePath: zipFilePath, password: "password", progress: { (progress) -> () in
print(progress)
}) //Zip