后台使用单个 NSURLSession uploadTaskWithRequest 上传多张图片 [英] Background upload multiple images using single NSURLSession uploadTaskWithRequest
问题描述
我想使用单个 uploadTaskWithRequest
方法在后台上传多张图片.在尝试以下代码时,后台会话不支持来自 NSData 的上传任务...请问如何实现这一点
I want to upload multiple images in background using a single uploadTaskWithRequest
method. While trying the following code returns Upload tasks from NSData are not supported in background sessions...please how to achieve this
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)
")
body.appendString("Content-Disposition: form-data; name="(key)"
")
body.appendString("(value)
")
}
}
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)
")
body.appendString("Content-Disposition: form-data; name="(self.filePathKey)"; filename="(filename)"
")
body.appendString("Content-Type: (mimetype)
")
body.appendData(data!)
body.appendString("
")
i += 1;
}
body.appendString("--(boundary)--
")
// 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();
}
推荐答案
要在后台会话中上传,必须先将数据保存到文件中.
To upload in a background session, the data must first saved to a file.
- 使用 writeToFile:options:.
- 调用 NSURLSession uploadTaskWithRequest:fromFile: 创建任务.请注意,请求中不能包含
HTTPBody
中的数据,否则上传会失败. - 在 URLSession:didCompleteWithError: 委托方法.
- Save the data to file using writeToFile:options:.
- Call NSURLSession uploadTaskWithRequest:fromFile: to create the task. Note that the request must not contain the data in the
HTTPBody
otherwise the upload will fail. - Handle completion in the URLSession:didCompleteWithError: delegate method.
您可能还想处理应用在后台时完成的上传.
You may also want to handle uploads which complete while the app is in the background.
- 实现 application:handleEventsForBackgroundURLSession:completionHandler 在 AppDelegate 中.
- 使用提供的标识符创建一个 NSURLSession.
- 按照通常的上传响应委托方法(例如处理 URLSession:didCompleteWithError:)
- 调用 URLSessionDidFinishEventsForBackgroundURLSession 完成事件处理后.
- Implement application:handleEventsForBackgroundURLSession:completionHandler in the AppDelegate.
- Create an NSURLSession with the provided identifier.
- Respond to the delegate methods as per a usual upload (e.g. handle the response in URLSession:didCompleteWithError:)
- Call URLSessionDidFinishEventsForBackgroundURLSession when you have completed processing the event.
为了更容易管理,为每个上传任务创建一个 NSURLSession
,每个任务都有一个唯一的标识符.
To make this easier to manage, create one NSURLSession
per upload task, each with a unique identifier.
参考URL 会话编程指南了解实现细节.
AppDelegate 示例:
Example 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 消息必须保存到单个文件中,该文件是上传到服务器的文件.
To upload multiple images in a single request, the images must first be encoded into the multipart/formdata MIME type, as you have done. The difference being that this entire MIME message must be saved to a single file, which is the file that is uploaded to the server.
这里是一个例子,展示了如何做到这一点.它通过将 MIME 部分直接序列化为文件来工作.您也可以在 NSData 中构建消息,尽管在处理大文件时可能会遇到内存限制.
Here is an example which shows how to do this. It works by serialising the MIME parts directly to a file. You could also build up the message in an NSData, although you risk running into memory limitations when handling large files.
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 = "
"
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)
}
这篇关于后台使用单个 NSURLSession uploadTaskWithRequest 上传多张图片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!