Ios 如何解压包含一个文件的大zip文件,并使用swift以字节为单位获得进度?
我尝试解压一个只包含一个项目(超过100MB)的大zip文件,并希望在解压过程中显示进度 我找到了一些解决方案,可以根据解压文件的数量来确定进度,但就我而言,我只有一个大文件在里面。所以我猜它一定是由解压的字节数决定的 实际上,我正在使用SSZipArchive,其代码如下:Ios 如何解压包含一个文件的大zip文件,并使用swift以字节为单位获得进度?,ios,swift,streaming,archive,unzip,Ios,Swift,Streaming,Archive,Unzip,我尝试解压一个只包含一个项目(超过100MB)的大zip文件,并希望在解压过程中显示进度 我找到了一些解决方案,可以根据解压文件的数量来确定进度,但就我而言,我只有一个大文件在里面。所以我猜它一定是由解压的字节数决定的 实际上,我正在使用SSZipArchive,其代码如下: var myZipFile:NSString="/Users/user/Library/Developer/CoreSimulator/Devices/mydevice/ziptest/testzip.zip";
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);
我没有找到解决办法
有人有提示、样本或样本链接吗
更新:
下面的代码看起来可以按预期工作,但是当只解压缩一个文件时,处理程序将只被调用一次(在解压缩结束时):
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)
}
})
}
您可以尝试以下代码:
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)
}
})
我希望我帮助过你
Ysee要实现您想要的,您必须修改SSZipArchive的内部代码 使用minizip提供压缩功能。您可以在此处看到minizip解压缩API: 在SSZipArchive.m中,您可以获取从中解压缩的文件的未压缩大小 您可以看到正在读取解压缩的内容:
您需要
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已经六年没有更新了,你需要一个新的选择 :用于压缩和解压缩文件的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
您使用的解压库是什么?我与其他SSZipArchive一起使用过。如果我正确理解SSZipArchive的源代码,则每个文件只调用一次进度处理程序,并且在解压单个文件时不提供任何获取进度的选项。感谢您的确认。这就是我所怀疑的。有没有其他方法可以做到这一点?我的意思是,我没有固定到SSZipArchive。@mcfly soft,我认为这可以在zlib的帮助下实现。非常感谢您的帮助。我会尽快查的。听起来很有希望。谢谢你的帮助,但它不能正常工作。当我调试代码时,它只会调用progressHandler一次。此时,Zipfile被解压缩。在我的例子中,zip中只有一个文件,所以我猜它只会为每个解压缩的文件调用处理程序。在我的例子中,这意味着它将在解压结束时被调用,并且在解压期间不会显示任何进度。有什么建议吗?我在问题上粘贴了我的实际代码。我通过删除UIProgressView简化了代码。我们还可以在控制台中看到println.No的进度。println可以工作,但只能调用一次。我正在寻找一个解决方案,在解压一个只有一个大文件的大zipfile时,我可以看到它的进度。如果您在一个zipfile中压缩了很多小文件,建议的解决方案将显示进度,但对于只包含一个文件的zipfile,则不会显示进度。非常感谢您的帮助。我想这是正确的答案,这就是我接受它的原因。我没有实际尝试,但我看到了解决办法。当然,如果有人能将代码与委托一起粘贴到SSZipArchive.m中,我也不会不高兴,因为我更熟悉swift:-)另一个建议:如果您对Obj-C不满意,可以直接使用minizip(而不是使用SSZipArchive)-swift可以直接链接到C代码。我实现了解决方案。(有点不同)它是有效的。我更新了问题中的更改。非常感谢你再次帮助我。看起来不错。这应该可以,只要你只处理一个文件的拉链。一个小更正:您可能想说
totalbytesread=totalbytesread+readBytes代码>以使totalbytesread
永远不会超过未压缩的大小(因此进度永远不会超过100%)。
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
}
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