繁体   English   中英

从URL异步下载和上传数据

[英]Downloading & Uploading Data From URL Swift Asynchronously

我有5个链接数组,其中包含jpgmov文件。 我想将它们下载到我的iOS Swift应用中。 我使用以下代码。

var index = 0
while (index < myLinks.count) {
if (myLinks[index].resource_type == "image") {
    let stringURL = "http://www.blablabla" + ".jpg"
    let url = NSURL(string : stringURL)
    getDataFromUrl(url!) { (data, response, error)  in
        guard let data = data where error == nil else { return }
        print(response?.suggestedFilename ?? url!.lastPathComponent ?? "")
        print("Download Finished")
        myLinks[index].image = UIImage(data: data)
    }
}
else if (myLinks[index].resource_type == "video") {
    let stringURL = "http://www.blablabla" + ".mov"
    let url = NSURL(string : stringURL)
    getDataFromUrl(url!) { (data, response, error)  in
        guard let data = data where error == nil else { return }
        print(response?.suggestedFilename ?? url!.lastPathComponent ?? "")
        print("Download Finished")
        dispatch_async(dispatch_get_main_queue(), {
            let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
            let documentsDirectory : NSString = paths[0]
            let appFile = documentsDirectory.stringByAppendingPathComponent(myLinks[index].id! + ".m4v")
            do {
                try data.writeToFile(appFile, options: .AtomicWrite)
                myLinks[index].videoURL =  NSURL(fileURLWithPath: appFile)
            }
            catch _ {
                print("error saving video")
            }
        })
    }
}

index = index + 1
}


 func getDataFromUrl(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) {
    NSURLSession.sharedSession().dataTaskWithURL(url) {
        (data, response, error) in
        completion(data: data, response: response, error: error)
        }.resume()
}

现在唯一的问题是,在下载这些资源之后,我想以幻灯片形式向用户显示它们,因此,只有知道所有5个下载都已完成,我才能继续进行。 上面的代码的问题在于,由于getDataFromUrl函数是异步执行的,因此while循环将在所有内容实际下载之前完成执行。

编辑

这也是使接受的答案也适合上传的代码。 问题在于,当队列中有多个值时,只有第一个任务被执行,然后什么也没有发生。

 var uploadQueue:[UploadMessage]?
let session = NSURLSession.sharedSession()
let lockQueue = dispatch_queue_create("com.dsdevelop.lockQueue", nil)

func getRemainingActiveUploads() -> Int {
    return (self.uploadQueue != nil) ? self.uploadQueue!.count : 0
}

func removeMessageFromUploadQueue(messageToBeRemoved : UploadMessage) {
    if (uploadQueue != nil) {
        dispatch_sync(lockQueue) {
            self.uploadQueue = self.uploadQueue?.filter({$0.date!.compare(messageToBeRemoved.date!) == NSComparisonResult.OrderedSame})
        }
    }
}

var uploadTimer : NSTimer?

func finishedUploading() {
    uploadTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(uploadAllLinks), userInfo: nil, repeats: false)
    if (needToRefetch) {
        needToRefetch = false
        newMessageReceived()
    }
}

func uploadAllLinks()
{
    print("uploading test")
    uploadTimer?.invalidate()
    uploadTimer = nil
    // suspending queue so they don't all finish before we can show it
    session.delegateQueue.suspended = true
    session.delegateQueue.maxConcurrentOperationCount = 1

    let myUrl = NSURL(string: "http://****")

    // create tasks
    if (uploadQueue != nil) {
        if (uploadQueue?.count > 0) {
        for message in uploadQueue!
        {
            let request = NSMutableURLRequest(URL:myUrl!)
            request.HTTPMethod = "POST"
            request.timeoutInterval = 10
            request.HTTPShouldHandleCookies=false

            var postString = "sender=" + message.sender! 
            request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);

            let dltask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) in
                if data != nil
                {
                    do {
                        let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
                        dispatch_async(dispatch_get_main_queue(), {

                            if let errorToken = jsonArray["error"] as! Bool? {
                                if  !errorToken  {
                                  self.uploadQueue = self.uploadQueue!.filter({$0.date!.compare(message.date!) != NSComparisonResult.OrderedSame})
                                                let remaining = self.getRemainingActiveUploads()
                                                print("Downloaded.  Remaining: \(remaining)")
                                                if (remaining == 0) {
                                                    self.finishedUploading()
                                                }
                                }
                                else {

                                                let remaining = self.getRemainingActiveUploads()
                                                print("Downloaded.  Remaining: \(remaining)")
                                                if (remaining == 0) {
                                                    self.finishedUploading()
                                                }
                                }
                            }
                            else {

                                            let remaining = self.getRemainingActiveUploads()
                                            print("Downloaded.  Remaining: \(remaining)")
                                            if (remaining == 0) {
                                                self.finishedUploading()
                                            }
                            }

                        })
                    }
                    catch {
                        print("Error: \(error)")
                    }
                }

            })
            print("Queuing task \(dltask)")
            dltask.resume()
        }
            session.delegateQueue.suspended = false
        }
        else {
            finishedUploading()
        }
        // resuming queue so all tasks run
    }

}

使用NSURLSession及其基础的NSOperationQueue的内置功能,您可以很容易地做到这一点。

import UIKit

class ViewController: UIViewController {

    let MyDownloadsCompleteNotification:String = "MyDownloadsCompleteNotification"

    var myLinks:[NSURL]?
    var downloadQueue:[NSURL]?
    let session = NSURLSession.sharedSession()
    let lockQueue = dispatch_queue_create("com.dsdevelop.lockQueue", nil)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        myLinks = [ NSURL(string: "https://pbs.twimg.com/profile_images/447374371917922304/P4BzupWu.jpeg")!,
                    NSURL(string: "http://www.telegraph.co.uk/content/dam/pets/2016/03/18/bunny-large_trans++qVzuuqpFlyLIwiB6NTmJwfSVWeZ_vEN7c6bHu2jJnT8.jpg")!,
                    NSURL(string: "http://4.bp.blogspot.com/-HTvSYzA-pO4/UgQb4Zh_u0I/AAAAAAAAEuI/XwhtogT_1tA/s1600/3+cute2.jpg")!,
                    NSURL(string: "http://cdn.shopify.com/s/files/1/0224/1915/files/bunny.jpg?22110")!,
                    NSURL(string: "http://vignette1.wikia.nocookie.net/hare/images/1/1f/Bunnies-bunny-rabbits-16437969-1280-800.jpg/revision/latest?cb=20130831183111")!
        ]


        // Register to know when all downloads are done
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(allDownloadsComplete), name: MyDownloadsCompleteNotification, object: nil)

        downloadAllLinks(myLinks!)
    }

    func allDownloadsComplete(n:NSNotification)
    {
        print("Awesome all downloads are done!")
    }

    func getRemainingActiveDownloads() -> Int
    {
        return (self.downloadQueue != nil) ? self.downloadQueue!.count : 0
    }

    func removeUrlFromDownloadQueue(url:NSURL)
    {
        if self.downloadQueue != nil
        {
            dispatch_sync(lockQueue) {
                self.downloadQueue = self.downloadQueue!.filter({$0.absoluteString == url.absoluteString})
            }
        }
    }

    func downloadAllLinks(links:[NSURL])
    {
        // suspending queue so they don't all finish before we can show it
        session.delegateQueue.suspended = true
        session.delegateQueue.maxConcurrentOperationCount = 1

        downloadQueue = links

        // create tasks
        for link in links
        {
            let dltask = session.downloadTaskWithURL(link, completionHandler: { (url, response, error) in
                if let urlString = response?.URL?.absoluteString
                {
                    self.downloadQueue = self.downloadQueue!.filter({$0.absoluteString != urlString})
                    let remaining = self.getRemainingActiveDownloads()
                    print("Downloaded.  Remaining: \(remaining)")

                    if (remaining == 0)
                    {
                        NSNotificationCenter.defaultCenter().postNotificationName(self.MyDownloadsCompleteNotification, object: nil)
                    }

                }
            })
            print("Queuing task \(dltask)")
            dltask.resume()
        }

        // resuming queue so all tasks run
        session.delegateQueue.suspended = false
    }
}

您可以创建一个计时器来检查所有5个资源是否都已完全下载。 将间隔设置为1ms,一旦完成所有5个操作,您将立即知道。

您应该添加一个var downloadsCompletedCounter = 0并在完成处理程序中增加此计数器:

self.downloadsCompletedCounter =+ 1 

dispatch_async(dispatch_get_main_queue()的块中使用此函数,以避免多线程问题。

您还可以为downloadsCompletedCounter实现一个属性观察器,以便确切知道下载何时完成:

var downloadsCompletedCounter = 0 {
    didSet{
        if downloadsCompletedCounter == 4 {
            print("Downloads completed")
            // call the function you want to execute after downloads complete
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM