2016-01-23 32 views
1

我写一个斯威夫特iOS应用下载前(我第一次,所以请多多包涵),我用更快的HTTP服务器来处理各种请求。其中一个请求是HTTP POST,其中包含一个JSON数组,用于指定要从Web上下载的图像(以及其他一些内容,与当前问题无关)。斯威夫特(IOS),等待所有图像来完成返回

我使用Alamofire下载图像(这工作正常),但我正在寻找好的(最好是简单的)方式来等待所有图像完成下载之前返回对POST请求的响应(因为响应必须包含指示结果的JSON,包括任何失败的下载)。

什么是实现这个(优选W/O型阻塞主线程)的好方法?

下面是一些片段来说明:

public func webServer(publicDir: String?) -> HttpServer { 
    let server = HttpServer() 

    server.POST["/images/update"] = { r in 
     let images = ...(from JSON array in body) 
     let updateResult = ImageUtil.updateImages(images) 
     let resultJson: String = Mapper().toJSONString(updateResult, prettyPrint: true)! 

     if updateResult.success { 
      return .OK(.Text(resultJson)) 
     } 
     return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](updateResult.errorMessage.utf8)) }) 
    } 
} 

static func updateImages(images: [ImageInfo]) -> UpdateResult { 
    let updateResult = UpdateResult() 
    for image in images { 
    Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath } 
     .validate() 
     .response{_, _, _, error in 
      if let error = error { 
       Log.error?.message("Error downloading file \(image.imageUrl) to \(image.fileName): \(error)") 
      } else { 
       updateResult.filesDownloaded++ 
       Log.info?.message("Downloaded file \(image.imageUrl) to \(image.fileName)") 
      }} 
    } 

    return updateResult // It obviously returns before any images finish downloading. I need to wait until all images have downloaded before I can return an accurate result. 
} 

更新2016年1月23日,使用每bbum

这是使用分配机制的尝试调度,但调用updateImages仍然立即返回(即使在使用dispatch_sync时)。

我怎么能等待所有下载我的HTTP响应返回给调用之前完成?

public func webServer(publicDir: String?) -> HttpServer { 
    let server = HttpServer() 

    server.POST["/images/update"] = { r in 
     let imageDownloader = ImageDownloader() 
     imageDownloader.updateimageFiles(adFilesOnServer) 
     let resultJson: String = Mapper().toJSONString(imageDownloader.updateResult, prettyPrint: true)! 

     if imageDownloader.updateResult.success { 
      return .OK(.Text(resultJson)) 
     } 
     return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](imageDownloader.updateResult.errorMessage.utf8)) }) 
    } 
} 

class ImageDownloader { 

    var updateResult = AdUpdateResult() 
    private var imageFilesOnServer = [ImageFile]() 

    private let fileManager = NSFileManager.defaultManager() 
    private let imageDirectoryURL = NSURL(fileURLWithPath: Settings.imageDirectory, isDirectory: true) 

    private let semaphore = dispatch_semaphore_create(4) 
    private let downloadQueue = dispatch_queue_create("com.acme.downloader", DISPATCH_QUEUE_SERIAL) 

    func updateimageFiles(imageFilesOnServer: [ImageFile]) { 
     self.imageFilesOnServer = imageFilesOnServer 

     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 

     for serverFile in imageFilesOnServer { 
      downloadImageFileFromServer(serverFile) 
     } 

     dispatch_sync(downloadQueue) { 
      dispatch_sync(dispatch_get_main_queue()) { 
       print("done") // It gets here before images have downloaded. 
      } 
     } 
    } 

    private func downloadImageFileFromServer(serverFile: ImageFile) { 

     let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName) 

     Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath } 
     .validate() 
     .response { _, _, _, error in 
      if let error = error { 
       Log.error?.message("Error downloading file \(serverFile.imageUrl) to \(serverFile.fileName): \(error)") 
      } else { 
       self.updateResult.filesDownloaded++ 
       Log.info?.message("Downloaded file \(serverFile.imageUrl) to \(serverFile.fileName)") 
      } 
      dispatch_semaphore_signal(self.semaphore) 
     } 
    } 
} 
+3

不要等待,使用完成处理或通知。 – vadian

+0

将每个添加到一个队列中,这个队列有一个依赖的最终操作,它持有完成逻辑 – Wain

+0

@vadian我正在处理一个HTTP POST请求,我需要发回一个包含完整下载结果的响应。我没有看到如何在没有等待的情况下实现这一点。 –

回答

0

首先,你真的不希望被发射了没有某种节气门的请求,每个图像。信号量在这类事情上工作得很好。

其次,你需要基本计算操作优秀的号码,然后开火完成处理程序时,他们都做。或者,如果可以随时启动新的操作,则可能需要对操作进行分组。

因此,伪代码:

sema = dispatch_semaphore_create(4) // 4 being # of concurrent operations allowed 
serialQ = dispatch_queue_create(.., SERIAL) 

dispatch_async(serialQ) { 
    dispatch_semaphore_wait(sema, FOREVER) // will block if there are 4 in flight already 
    for image in images { 
     downloader.downloadAsync(image, ...) { // completion 
       dispatch_semaphore_signal(sema) // signal that we are done with one 
       ... handle downloaded image or error ... 
       ... add downloaded images to downloadedImages ... 
     } 
    } 
} 

dispatch_async(serialQ) { 
    // since serialQ is serial, this will be executed after the downloads are done 
    dispatch_async(main_queue()) { 
     yo_main_queue_here_be_yer_images(... downloadedImages ...) 
    } 
} 
+0

感谢您的回复。我试着实现它,但是我必须缺少一些东西,因为我仍然无法让它等待所有文件下载,以便我可以向调用者返回适当的HTTP响应。我用我的代码的简化版本更新了这个问题 - 也许你可以提供一些额外的指导? –

+0

我只是想更换Alamofire下载与NSThread.sleepForTimeInterval(5)作为测试要求,并且它的行为预期。也许问题在于Alamofire的下载方法在新线程上启动,使得分派的下载作业看起来立即完成?你的建议将不胜感激...... –

+0

@JimBalo除非下载时提前或重复调用完成块,这不应该是这样的。请注意,updateimageFiles()会在代码被写入时立即返回......而这正是您希望它执行的操作。您需要根据分派到主线程的块中的已完成下载进行更新。 – bbum