后台使用单个NSURLSession uploadTaskWithRequest上传多个图像

时间:2016-07-13 11:07:27

标签: ios swift background nsurlsession

我想使用单个uploadTaskWithRequest方法在后台上传多个图片。尝试以下代码时,后台会话不支持从NSData返回上传任务...请如何实现此

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();

}

1 个答案:

答案 0 :(得分:13)

要在后台会话中上传,必须先将数据保存到文件中。

  1. 使用writeToFile:options:将数据保存到文件。
  2. 致电NSURLSession uploadTaskWithRequest:fromFile:以创建任务。请注意,请求不得包含HTTPBody中的数据,否则上传将失败。
  3. 使用URLSession:didCompleteWithError:委托方法处理完成。
  4. 您可能还希望处理在应用在后台时完成的上传。

    1. 在AppDelegate中实施application:handleEventsForBackgroundURLSession:completionHandler
    2. 使用提供的标识符创建NSURLSession。
    3. 按照常规上传方式回复委托方法(例如,处理URLSession:didCompleteWithError:中的回复)
    4. 完成事件处理后,请致电URLSessionDidFinishEventsForBackgroundURLSession
    5. 为了便于管理,请为每个上传任务创建一个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.
          }
      }
      

      要在单个请求中上传多个图像,必须首先将图像编码为multipart / 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)
      }