我的应用会在前台启动将内容上传到服务器。由于此过程可能需要一些时间,因此用户可以很好地将应用程序转换为后台。
我使用AFHTTPSessionManager提交上传内容:
let sessionManager = AFHTTPSessionManager()
sessionManager.requestSerializer.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-type")
sessionManager.POST(urlStr, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
}, progress: { (progress) -> Void in
}, success: { (task, responseObject) -> Void in
print(responseObject)
success(responseObject: responseObject)
}, failure: { (task, error) -> Void in
print(error)
if let errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
do {
let json = try NSJSONSerialization.JSONObjectWithData(errorData, options: NSJSONReadingOptions.MutableContainers)
failure(error: error, responseObject: json)
} catch let error {
print(error)
}
}
})
我需要此过程在应用处于后台状态时继续,直到完成。有一个很好的答案here让我走上正轨,但我仍然在某些地方处于黑暗中。我尝试过使用" BackgroundSessionManager"从链接的答案中将类更改为我的上传调用:
BackgroundSessionManager.sharedManager().POST(NetworkManager.kBaseURLString + "fulfill_request", parameters: params, constructingBodyWithBlock: { (formData) -> Void in
formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
}, progress: nil, success: nil, failure: nil)?.resume()
并将此添加到我的AppDelegate:
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
BackgroundSessionManager.sharedManager().savedCompletionHandler = completionHandler
}
但是我在某些后台线程上遇到EXC_BAD_ACCESS崩溃,几乎没有信息。我是否正确接近这个?
答案 0 :(得分:3)
有几点意见:
您正在构建多部分请求,但随后强制Content-Type
标头为application/x-www-form-urlencoded
。但这不是多部分请求的有效内容标题。我建议删除请求标头的手动配置(或告诉我们你为什么这样做)。 AFNetworking将为您设置Content-Type
。
您可能只想在用户离开应用程序后请求一点时间来完成请求,而不是费心去处理后台会话。
var backgroundTask = UIBackgroundTaskInvalid
func uploadImageWithData(dataObject: NSData, mimeType: String, urlStr: String, params: [String: String]?, success: (responseObject: AnyObject?) -> (), failure: (error: NSError, responseObject: AnyObject) -> ()) {
let app = UIApplication.sharedApplication()
let endBackgroundTask = {
if self.backgroundTask != UIBackgroundTaskInvalid {
app.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
}
}
backgroundTask = app.beginBackgroundTaskWithName("com.domain.app.imageupload") {
// if you need to do any addition cleanup because request didn't finish in time, do that here
// then end the background task (so iOS doesn't summarily terminate your app
endBackgroundTask()
}
let sessionManager = AFHTTPSessionManager()
sessionManager.POST(urlStr, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
}, progress: { (progress) -> Void in
}, success: { (task, responseObject) -> Void in
print(responseObject)
success(responseObject: responseObject)
endBackgroundTask()
}, failure: { (task, error) -> Void in
print(error)
if let errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
do {
let json = try NSJSONSerialization.JSONObjectWithData(errorData, options: NSJSONReadingOptions.MutableContainers)
failure(error: error, responseObject: json)
} catch let error {
print(error)
}
}
endBackgroundTask()
})
}
我建议添加exception breakpoint并查看是否可以帮助您追踪违规行。
坦率地说,如果异常在NSURLSession
内部处理中异步发生,这可能没有帮助,但这是我尝试追踪异常源时尝试的第一件事。
如果你真的觉得你必须使用后台会话(即你的上传任务可能需要超过beginBackgroundTaskWithName
允许的三分钟),那么请注意后台任务必须是上传或下载任务。
但是,我相信POST
方法会创建一个数据任务。在iOS 7中,根本不允许将数据任务与后台会话一起使用。在iOS 8中,您可以使用后台任务启动数据任务,但您必须使用didReceiveResponse
回复NSURLSessionResponseBecomeDownload
,例如在configureDownloadFinished
的{{1}}中,您可以尝试添加:
BackgroundSessionManager
或者,如果您再次使用后台会话,则可能需要手动构建[self setDataTaskDidReceiveResponseBlock:^NSURLSessionResponseDisposition(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response) {
return NSURLSessionResponseBecomeDownload;
}];
对象,并将其作为上传或下载任务提交,而不是使用NSURLRequest
。 AFNetworking提供了解除请求构建和请求提交的机制,因此如果您需要走这条路,请告诉我们,我们可以告诉您如何做到这一点。
与使用后台POST
相比,请求一点时间完成请求的底线beginBackgroundTaskWithName
将更容易。如果绝对必要的话,只做后者(例如,你很有可能需要超过三分钟才能完成请求)。