有没有人成功链接NSURLSession后台上传?
我正在尝试使用NSURLSession的后台上传来上传5 MB部分的巨大视频文件。上传必须是有序的。整个事情在前景中运作良好。我正在使用AFNetwoking,它是一个多部分上传。但是当应用程序处于后台时,第一项上传正常并在后台启动第二项(在AFURLSessionManager的setDidFinishEventsForBackgroundURLSessionBlock中)。但它突然停止(我最好的猜测是在30秒内,因为在后台唤醒的应用程序的最大生命周期为30秒)然后没有任何反应。我预计第二次会议将在后台完成并调用第三次会议 - 这是一种连锁行为,但这似乎不起作用。
我尝试使用HTTPMaximumConnectionsPerHost = 1一次性将所有文件部分添加到单个NSURLSession中 - 这样可以正常工作并将部分文件上传到整个文件。但文件部分是按随机顺序挑选的,即第1部分上传,然后是第5部分,第3部分,第10部分等等。我尝试在NSOperationQueue中添加这个操作之间的依赖关系,这似乎弄乱了整个事情 - 上传根本不起作用。
我知道视频文件可以作为单个文件在后台上传,但服务器需要5 MB部分。因此,我想我唯一的选择是链接上传,或将所有部分添加到NSURLSession,但要确保它们始终按照添加的顺序上传。
任何帮助都将不胜感激。
我的代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[NSString stringWithFormat:@"%d", rand()]];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:config];
config.HTTPMaximumConnectionsPerHost = 1;
[manager setDidFinishEventsForBackgroundURLSessionBlock:^(NSURLSession *session) {
dispatch_async(dispatch_get_main_queue(), ^{
// Call the completion handler to tell the system that there are no other background transfers.
// completionHandler();
[self upload];
});
}];
}
- (IBAction)start:(id)sender {
[self upload];
}
-(void) upload {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Sample" ofType:@"mp4"];
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:@"234", @"u", @"Sample.mp4", @"f",nil];
NSMutableURLRequest *request = [serializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"data" fileName:@"Sample.mp4" mimeType:@"video/mp4" error:nil];
} error:nil];
__block NSString *tempMultipartFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"Test"];
tempMultipartFile = [tempMultipartFile stringByAppendingString:[NSString stringWithFormat:@"%d", rand()]];
NSURL *filePathtemp = [NSURL fileURLWithPath:tempMultipartFile];
__block NSProgress *progress = nil;
[serializer requestWithMultipartFormRequest:request writingStreamContentsToFile:filePathtemp completionHandler:^(NSError *error) {
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePathtemp progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSLog(@"Request--> %@.\n Response --> %@ \n%@", request.URL.absoluteString ,responseObject, error? [NSString stringWithFormat:@" with error: %@", [error localizedDescription]] : @""); //Lets us know the result including failures
[[NSFileManager defaultManager] removeItemAtPath:tempMultipartFile error:nil];
}];
[uploadTask resume];
[manager setTaskDidSendBodyDataBlock:^(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
NSLog(@"uploading");
}];
}];
}
答案 0 :(得分:5)
好吧,最后我联系Apple了解关于链接后台上传的说明 - 这在iOS中是不可能的.NSURLSession有一个恢复速率限制器,它阻止应用程序在后台执行链式任务,如https://forums.developer.apple.com/thread/14854中所述。相反,apple建议批量转移或其他选项,如https://forums.developer.apple.com/thread/14853。我要问的另一件事是在上传队列中订购多个任务 - 即强制NSURLSession按照添加顺序上传任务。正如dgatwood指出的那样,使用NSOperationQueue是不可能的,Apple也提到了相同的内容。正如eskimo在回复邮件&#34中所提到的那样; NSURLSession并不保证按顺序运行您的请求而且没有强制执行的方式。&#34;所以我在原来的问题上几乎没有选择。
答案 1 :(得分:0)
NSOperationQueue会在你的应用程序运行时消失,在你将它放入后台后不会太久。这样做不会很好。
相反,根据您的个人喜好,按顺序存储要上传的文件列表 - 在磁盘上的文件中或 NSUserDefaults 中。然后,在后台会话中使用上载任务来启动第一个任务。完成后,如果您的应用程序未运行,它应该会在后台自动重新启动以处理数据。
要支持此行为,请在应用程序:handleEventsForBackgroundURLSession:completionHandler:方法中,像您最初一样重新创建后台会话,并存储完成处理程序。
此后不久,应该调用您的请求的委托方法,就好像您的应用程序在下载完成时仍在运行一样。除其他外,这些方法可以为您的应用程序提供来自服务器的响应数据,响应对象(用于检查状态代码)等。
当你得到didCompleteWithError委托调用(成功时为nil,IIRC),如果传输失败,再试一次或其他。如果成功,则开始上传下一个并更新磁盘上的文件列表。
无论哪种方式,当您调用会话委托的URLSessionDidFinishEventsForBackgroundURLSession:**方法时,请调用之前存储的处理程序,大致如下:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
storedHandler();
}];
通过致电完成处理程序,您告诉操作系统您不需要继续运行。
冲洗,重复。
如果您的应用在请求完成后仍在运行,则所有操作都如上所述,除非您没有获得应用程序:handleEventsForBackgroundURLSession:completionHandler:或 URLSessionDidFinishEventsForBackgroundURLSession: 电话,这意味着您不必存储完成处理程序或调用它。
有关详细信息,请参阅URL Session Programming Guide。