【问题标题】:Background upload multiple images using single NSURLSession uploadTaskWithRequest后台使用单个 NSURLSession uploadTaskWithRequest 上传多张图片
【发布时间】:2016-07-13 11:07:26
【问题描述】:

我想使用一个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();

}

【问题讨论】:

    标签: ios swift background nsurlsession


    【解决方案1】:

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

    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.
        }
    }
    

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

    【讨论】:

    • 但是我有很多图像表单数据,如何处理?
    • 请更新您的问题以澄清您要做什么。就目前而言,您的问题没有提及有关多张图片的任何内容。
    • 更新了答案以显示如何将多个图像文件编码为单个 MIME 多部分消息文件以进行上传。
    • @KaranveerSingh 数据写入file.writeData(partData!)。当系统空间不足时,iOS 将自动删除缓存目录中的文件。或者您可以使用NSFileManager 删除文件。
    • @KaranveerSingh 我重新阅读了您的评论并注意到您指的是第一个示例。我已编辑答案以包含使用NSData.writeToURL:atomically: 写入文件的行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2017-04-08
    • 2014-03-19
    • 2011-12-10
    • 2015-02-17
    相关资源
    最近更新 更多