首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何解压一个包含一个文件的大zip文件,并使用swift获得以字节为单位的进度?

如何解压一个包含一个文件的大zip文件,并使用swift获得以字节为单位的进度?
EN

Stack Overflow用户
提问于 2015-05-14 15:57:26
回答 4查看 4.5K关注 0票数 16

我尝试解压一个只包含一个项目(超过100MB)的大zip文件,并且喜欢在解压过程中显示进度。

我找到了解决方案,可以根据解压缩的文件量来确定进度,但在我的例子中,我只有一个大文件。所以我猜它一定是由解压缩后的字节数决定的?

实际上,我使用的是SSZipArchive和以下代码,它运行良好:

代码语言:javascript
复制
    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);

我没有找到解决这个问题的办法。

有没有人有提示,样本或样本的链接?

更新:下面的代码看起来会像预期的那样工作,但是当只有一个文件被解压时,处理程序将只被调用一次(在解压结束时):

代码语言:javascript
复制
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,如下所示。也许其他人也可以使用这个:

代码语言:javascript
复制
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;
                    }
                }
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-05-26 18:46:55

要实现您想要的功能,您必须修改SSZipArchive的内部代码。

SSZipArchive使用minizip来提供压缩功能。你可以在这里看到迷你解压API:unzip.h

在SSZipArchive.m中,您可以从fileInfo variable中获取正在解压缩的文件的解压缩大小。

您可以看到,正在读取here解压后的内容

代码语言:javascript
复制
 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添加一个新委托,以便将这些数据发送回调用代码。

票数 2
EN

Stack Overflow用户

发布于 2015-05-22 17:54:59

你可以试试这段代码:

代码语言:javascript
复制
    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

票数 2
EN

Stack Overflow用户

发布于 2019-03-25 23:45:29

据我所知,最明显的答案是修改SSZipArchive的内部代码。但我决定走不同的路,并写了这个扩展。它很容易理解,但请不要犹豫提出任何问题。

此外,如果您认为我的解决方案有缺陷,或者您知道如何改进它,我将很高兴听到。

这里有一个解决方案:

代码语言:javascript
复制
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)
    }
}

方法就是这样使用的:

代码语言:javascript
复制
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秒的间隔更新文件大小信息。我不知道获取文件元数据是否会导致性能过载,而且我找不到任何关于这方面的信息。

无论如何,我希望我能帮助一些人:)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30232207

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档