2016-07-13 97 views
7

多個圖像我想用一個單一的uploadTaskWithRequest方法上載在背景的多個圖像。在嘗試下面的代碼返回NSData的從上傳任務在後臺會話不支持......請如何實現這一點背景上傳使用單NSURLSession uploadTaskWithRequest

func createRequest (param : NSDictionary ,imagearray :NSMutableArray, strURL : String) -> NSURLRequest { 

    let boundary = generateBoundaryString() 

    let url = NSURL(string: strURL) 
    let request = NSMutableURLRequest(URL: url!) 

    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 
    request.HTTPMethod = "POST" 
    request.HTTPBody = createBodyWithParameters(param, image_array:imagearray,boundary: boundary); 

    return request 
} 

    func createBodyWithParameters(parameters: NSDictionary,image_array:NSMutableArray,boundary: String) -> NSData { 
let body = NSMutableData()   
for (key, value) in parameters { 
     if(value is String || value is NSString){ 
      body.appendString("--\(boundary)\r\n") 
      body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n") 
      body.appendString("\(value)\r\n") 
     } 
    } 
    var i = 0; 
    for image in image_array { 
     let filename = "image\(i).jpg" 
     let data = UIImagePNGRepresentation(image as! UIImage); 
     let mimetype = "image/png" 
     body.appendString("--\(boundary)\r\n") 
     body.appendString("Content-Disposition: form-data; name=\"\(self.filePathKey)\"; filename=\"\(filename)\"\r\n") 
     body.appendString("Content-Type: \(mimetype)\r\n\r\n") 
     body.appendData(data!) 
     body.appendString("\r\n") 
     i += 1; 
    } 

    body.appendString("--\(boundary)--\r\n") 
    //  NSLog("data %@",NSString(data: body, encoding: NSUTF8StringEncoding)!); 
    return body 
} 

func postrequestwithformdata(requesturl:String,postparams:NSDictionary,postformadata:NSMutableArray,requestId:Int) 
{ 

    self.requestid = requestId; 
    let requestformdata = self.createRequest(postparams, imagearray: postformadata, strURL: requesturl); 
    let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(Contants.identifier) 
    let session: NSURLSession = NSURLSession(configuration:configuration, delegate: self, delegateQueue: NSOperationQueue.mainQueue()); 
    let task: NSURLSessionUploadTask = session.uploadTaskWithRequest(requestformdata, fromData: requestformdata.HTTPBody!); 
    task.resume(); 

} 

回答

10

在後臺上傳會話,數據必須首先保存到文件中。

  1. 使用writeToFile:options:將數據保存到文件。
  2. 呼叫NSURLSession uploadTaskWithRequest:fromFile:創建任務。請注意,該請求不得包含HTTPBody中的數據,否則上傳將失敗。
  3. 手柄完成在URLSession:didCompleteWithError:委託方法。

您可能還想要處理在應用程序處於後臺時完成的上傳。

  1. 在AppDelegate中執行application:handleEventsForBackgroundURLSession:completionHandler
  2. 用提供的標識符創建一個NSURLSession。
  3. 根據常規上傳響應代表方法(例如,處理URLSession:didCompleteWithError:中的響應)
  4. 當您完成處理事件時,請致電URLSessionDidFinishEventsForBackgroundURLSession

爲了便於管理,請爲每個上傳任務創建一個NSURLSession,每個上傳任務都有一個唯一標識。

參考URL Session Programming Guide的實施細則。

實施例的AppDelegate:

@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate { 

    var window: UIWindow? 

    typealias CompletionHandler =() -> Void 

    var completionHandlers = [String: CompletionHandler]() 

    var sessions = [String: NSURLSession]() 


    func upload(request: NSURLRequest, data: NSData) 
    { 
     // Create a unique identifier for the session. 
     let sessionIdentifier = NSUUID().UUIDString 

     let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first! 
     let fileURL = directoryURL.URLByAppendingPathComponent(sessionIdentifier) 

     // Write data to cache file. 
     data.writeToURL(fileURL, atomically: true); 

     let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(sessionIdentifier) 

     let session: NSURLSession = NSURLSession(
      configuration:configuration, 
      delegate: self, 
      delegateQueue: NSOperationQueue.mainQueue() 
     ) 

     // Store the session, so that we don't recreate it if app resumes from suspend. 
     sessions[sessionIdentifier] = session 

     let task = session.uploadTaskWithRequest(request, fromFile: fileURL) 

     task.resume() 
    } 

    // Called when the app becomes active, if an upload completed while the app was in the background. 
    func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: CompletionHandler) { 

     let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(identifier) 

     if sessions[identifier] == nil { 

      let session = NSURLSession(
       configuration: configuration, 
       delegate: self, 
       delegateQueue: NSOperationQueue.mainQueue() 
      ) 

      sessions[identifier] = session 
     } 

     completionHandlers[identifier] = completionHandler 
    } 

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { 

     // Handle background session completion handlers. 
     if let identifier = session.configuration.identifier { 

      if let completionHandler = completionHandlers[identifier] { 
       completionHandler() 
       completionHandlers.removeValueForKey(identifier) 
      } 

      // Remove session 
      sessions.removeValueForKey(identifier) 
     } 

     // Upload completed. 
    } 
} 

要上載在單個請求中的多個圖像時,圖像必須首先被編碼成多部分/ FORMDATA MIME類型,如已完成。不同的是,這整個MIME消息必須保存到單個文件,這是上傳到服務器的文件。

這裏是它展示瞭如何做到這一點的例子。它通過將MIME部分直接序列化到文件來工作。儘管在處理大文件時可能會遇到內存限制,但您也可以在NSData中構建消息。

func uploadImages(request: NSURLRequest, images: [UIImage]) { 

    let uuid = NSUUID().UUIDString 
    let boundary = String(count: 24, repeatedValue: "-" as Character) + uuid 

    // Open the file 
    let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first! 

    let fileURL = directoryURL.URLByAppendingPathComponent(uuid) 
    let filePath = fileURL.path! 

    NSFileManager.defaultManager().createFileAtPath(filePath, contents: nil, attributes: nil) 

    let file = NSFileHandle(forWritingAtPath: filePath)! 


    // Write each image to a MIME part. 
    let newline = "\r\n" 

    for (i, image) in images.enumerate() { 

     let partName = "image-\(i)" 
     let partFilename = "\(partName).png" 
     let partMimeType = "image/png" 
     let partData = UIImagePNGRepresentation(image) 

     // Write boundary header 
     var header = "" 
     header += "--\(boundary)" + newline 
     header += "Content-Disposition: form-data; name=\"\(partName)\"; filename=\"\(partFilename)\"" + newline 
     header += "Content-Type: \(partMimeType)" + newline 
     header += newline 

     let headerData = header.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) 

     print("") 
     print("Writing header #\(i)") 
     print(header) 

     print("Writing data") 
     print("\(partData!.length) Bytes") 

     // Write data 
     file.writeData(headerData!) 
     file.writeData(partData!) 
    } 

    // Write boundary footer 
    var footer = "" 
    footer += newline 
    footer += "--\(boundary)--" + newline 
    footer += newline 

    print("") 
    print("Writing footer") 
    print(footer) 

    let footerData = footer.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) 
    file.writeData(footerData!) 

    file.closeFile() 

    // Add the content type for the request to multipart. 
    let outputRequest = request.copy() as! NSMutableURLRequest 

    let contentType = "multipart/form-data; boundary=\(boundary)" 
    outputRequest.setValue(contentType, forHTTPHeaderField: "Content-Type") 


    // Start uploading files. 
    upload(outputRequest, fileURL: fileURL) 
} 
+0

但我有很多圖像形式的數據,如何處理這個? –

+0

請更新您的問題,以澄清您正在嘗試做什麼。就目前而言,你的問題沒有提到任何有關多個圖像的問題。 –

+0

查看[本教程](http://www.kaleidosblog.com/how-to-upload-images-using-swift-2-send-multipart-post-request),其中顯示瞭如何使用MIME編碼將多個圖像編碼成單個上傳請求。 –