Ios NSURLSession后台上载不工作
我正试图将一系列文件从iPhone上传到服务器,目的是让这些文件的上传即使在应用程序处于后台或挂起时也能进行 我正在使用Ios NSURLSession后台上载不工作,ios,objective-c,background-process,Ios,Objective C,Background Process,我正试图将一系列文件从iPhone上传到服务器,目的是让这些文件的上传即使在应用程序处于后台或挂起时也能进行 我正在使用nsursession及其系列API提供的后台传输 奇怪的是,它在两周前就完全工作了。例如: 我会点击UI上的“上传”按钮 文件将开始在我的服务器上逐个显示 我点击iPhone上的“主页”按钮,让应用程序进入后台 文件将继续上载到服务器,直到全部完成 过去几天,我一直在网络模块之外进行重构。几天前,当我再次尝试上传时,只要我点击上面第(3)步的“主页”按钮。当我再次进入应用程序
nsursession
及其系列API提供的后台传输
奇怪的是,它在两周前就完全工作了。例如:
init
方法中,我初始化NSURLSessionConfiguration
和NSURLSession
:
urlSessionConfigUpload = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kBACKGROUND_SESSION_CONFIG_ID];
urlSessionConfigUpload.sessionSendsLaunchEvents = YES;
urlSessionConfigUpload.discretionary = YES;
urlSessionConfigUpload.HTTPMaximumConnectionsPerHost = 8;
urlSessionConfigUpload.networkServiceType = NSURLNetworkServiceTypeBackground;
urlSessionConfigUpload.HTTPShouldUsePipelining = NO;
urlSessionConfigUpload.allowsCellularAccess = NO;
urlSession = [NSURLSession sessionWithConfiguration:urlSessionConfigUpload delegate:self delegateQueue:nil];
2) 有一个方便的方法调用来执行实际的上传。每个会话只有一个上载任务:
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setHTTPMethod:@"PUT"];
[urlRequest addValue:@"keep-alive" forHTTPHeaderField:@"Connection"];
[urlRequest addValue:contentType forHTTPHeaderField:@"Content-Type"];
// NB: for upload task in the background, uploadTaskWithRequest:fromData (explicit construction of HTTP POST body) can’t be used,
// must use uploadTaskWithRequest:fromFile (requiring HTTP PUT)
NSURLSessionDataTask *uploadTask = [urlSession uploadTaskWithRequest:urlRequest fromFile:[NSURL fileURLWithPath:filePath]];
[uploadTask resume];
3) 在didCompleteWithError
委托中,我检查所有文件是否已上载,如果未上载,则转到下一个文件-GLOBAL.uploadQueue
是我保存所有必须上载文件的引用的地方,GLOBAL.uploadQueueIndexNextFile
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error
{
if ((error == nil && (GLOBAL.uploadQueueIndexNextFile < GLOBAL.uploadQueue.count - 1)) {
// Not on last file, increment counter, start upload of next file
speedLoggerResult = [NSString stringWithFormat:@"Transferring %i of %i files", (GLOBAL.uploadQueueIndexNextFile + 1), GLOBAL.uploadQueue.count];
GLOBAL.uploadQueueIndexNextFile++;
[GLOBAL.fileProcessor processNextFileInUploadQueue];
}
}
很高兴在这一点上尝试任何东西。谢谢
编辑#1
当后台上传工作时观察到操作系统调用应用程序:handleEventsForBackgroundURLSession:completionHandler:。当它不起作用时,总是不回电话
我不确定后台上传的先决条件是否是操作系统必须先关闭应用程序。如果是,在什么条件下会发生这种情况?我们能不能催一下
正如前面提到的50次中有49次,操作系统会将应用程序保留在后台,并且上传会被暂停。有一件事需要澄清的是,你不能在后台长时间运行任何任务,因为苹果不允许你。只有在特殊情况下,苹果才会考虑。最好的解释是 现在回到你们的问题,关于你们的实现,它将只在后台工作,上传任务是在应用程序处于活动状态时启动的,但任务仍未完成。这就是为什么每50次尝试中就有1次任务在后台运行 现在,为了解决您的问题,您必须立即启动所有/一组上载,以便万一应用程序进入后台,您的应用程序仍能够上载文件。本文简要解释了与背景迁移相关的不同案例 你也可以尝试多部分上传请求
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
NSMutableURLRequest*请求=[[AFHTTPRequestSerializer]multipartFormRequestWithMethod:@“POST”URLString:@”http://example.com/upload“参数:nil constructingBodyWithBlock:^(id formData){
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@]file://path/to/image.jpg“]name:@“file”fileName:@“fileName.jpg”mimeType:@“image/jpeg”错误:nil];
}错误:无];
AFURLSessionManager*manager=[[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask*上传任务;
uploadTask=[manager]
uploadTaskWithStreamedRequest:请求
进度:^(NSProgress*_非空上传进度){
//这不会在主队列上回调。
//您负责将UI更新发送到主队列
dispatch\u async(dispatch\u get\u main\u queue()^{
//更新进度视图
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse*\u非空响应,id\u可空响应对象,NSError*\u可空错误){
如果(错误){
NSLog(@“错误:%@”,错误);
}否则{
NSLog(@“%@%@”,响应,响应对象);
}
}];
[上传任务恢复];
我的一个视频应用程序也有同样的问题。这个问题发生在2月14日之后。我挖掘了很多,发现这个问题发生在苹果改变了他们的全球证书之后。请检查此https://developer.apple.com/support/certificates/expiration/
。
解决方案是首先从keychain access吊销现有证书,然后添加新的开发/分发证书、新的应用程序id和配置文件。它肯定会起作用。我想问题出在上传部分。在完成上一个上载任务后,您将启动下一个上载任务,而不是一次创建所有上载任务。在appDelegate中,将来自
应用程序:handleEventsForBackgroundURLSession:completionHandler:
的完成处理程序作为属性存储。调用nsurlsessionelegate
urlsessiondifinisheventsforbackgroundurlsession:
nsurlsessionelegate的委托方法中的完成块。确保所有上载任务都已完成
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
@interface AppDelegate ()
@property (atomic) UIBackgroundTaskIdentifier bgTask;
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.bgTask = UIBackgroundTaskInvalid;
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (self.bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(startTracking) userInfo:nil repeats:YES];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// invalidate the timer if still running
[self.timer invalidate];
// end the background task if still present
if (self.bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
// Periodically informs the delegate of the progress of sending body content to the server.
// Compute progress percentage
float progress = (float)totalBytesSent / (float)totalBytesExpectedToSend;
// Compute time executed so far
NSDate *stopTime = [NSDate date];
NSTimeInterval executionTime = [stopTime timeIntervalSinceDate:startTime];
// Send info to console
NSLog(@"%s bytesSent = %lld, totalBytesSent: %lld, totalBytesExpectedToSend: %lld, progress %.3f, time (s): %.1f", __PRETTY_FUNCTION__, bytesSent, totalBytesSent, totalBytesExpectedToSend, progress*100, executionTime);
}