2016-01-23 31 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