NSURLSession后台上传不起作用 [英] NSURLSession background upload not working

查看:104
本文介绍了NSURLSession后台上传不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将iPhone上的一系列文件上传到服务器,目的是即使应用程序在后台或挂起时也要上传这些文件.

I'm trying to upload a series of files from iPhone up to a server, with the intent that the upload of these files happen even when the App is in the background or suspended.

我正在使用NSURLSession及其一系列API提供的后台传输.

I'm using background transfer provided by NSURLSession and its series of APIs.

奇怪的是,它已经在2周前完全正常工作了.如:

  1. 我会点击界面上的上传"按钮
  2. 文件将开始在我的服务器上一一出现
  3. 我点击iPhone上的主页"按钮,使该应用进入后台
  4. 文件将继续上传到服务器,直到全部完成

最近几天,我一直在网络模块之外进行一些重构.几天前,当我再次尝试上传时,点击首页"按钮后,请执行上面的步骤(3).当我再次进入应用程序时,文件上传将停止.上传将恢复.

Last couple of days, I've been doing some refactoring outside of the network module. When I tried the upload again a couple of days ago, as soon as I hit "Home" button, step (3) above. File upload would stop, when I go into the app again. Upload would resume.

似乎后台上传甚至无法正常工作(对于一个文件,更不用说多个文件了.)

我已经多次运行该代码,并发现它可以工作大约1/50次.但是其他49次却没有.我还检查了以前可以工作的代码版本(服务器+ iOS),它不再起作用-或说,工作原理很少变化(1/50)

I've ran the code multiple times, and found about 1 / 50 times it works. But the other 49 times it doesn't. I've also checked out the version of the code that used to work (both server + iOS), and it no longer works - or rather, works vary rarely (the 1/50)

已通过规则进行后台传输和多次 URL会话的生命周期,以确保我遵循苹果公司的建议准则,我正在绞尽脑汁想知道发生了什么,这让我感到困惑-我怀疑这不是代码实现.

Having gone through the rules for Background transfer and Lifecycle of URL Session multiple times to make sure I'm adhering by Apple suggested guidelines, I'm racking my brain as to what broke, it boggles the mind how illogical this is - I suspect it's something other than code implementation.

因此,感谢您的帮助...

So any help is appreciated...

1)在用于网络类(单例)的init方法中,我初始化了NSURLSessionConfigurationNSURLSession:

1) In init method for my network class (a singleton), I initialise the NSURLSessionConfiguration and 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)有一个方便的方法可以用来进行实际的上传.每个会话只能执行1个上传任务:

2) There's a convenience method called to do the actual upload. There is only ever 1 upload task per session:

    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

3) In the didCompleteWithError delegate, I check if all files have been uploaded, if not, move onto the next file - GLOBAL.uploadQueue is where I keep a reference to all files I have to upload, 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];
    }
}

processNextFileInUploadQueue将准备文件并调用便捷方法进行上传(上面的(2)).

processNextFileInUploadQueue will prepare the file and call the convenience method for upload ((2) above).

这是穴居人调试的一些示例输出(针对文件2-4). 注意,一旦应用进入后台,上传就会停止.

Here are some sample output from caveman debugging (for file 2 - 4). Notice, how as soon as app goes into background, upload stops.

注意,我等待的时间也超过了下面输出中显示的10秒.我最长的晚餐时间是30分钟,后来又回来了,上传的内容超时了.一旦应用程序进入后台,操作系统就再也不会选择它.

2016-02-21 05:53:01 +0000 | bkgd debug - about to start upload task | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 32768 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 65536 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 98304 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 131072 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 163840 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 196608 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 229376 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | totalBytesSent | 233546 of 233546 | queueIndex: 2

2016-02-21 05:53:01 +0000 | in networking | didCompleteWithError | queueindex: 2

bkgd debug - processing next file

2016-02-21 05:53:02 +0000 | bkgd debug - about to start upload task | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 32768 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 65536 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 98304 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 131072 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 163840 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 196608 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 229376 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 262144 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 294912 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 327680 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 360448 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 387704 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 391392 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 393216 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 425984 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 458752 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 491520 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 524288 of 1231286 | queueIndex: 3

2016-02-21 05:53:02 +0000 | in networking | totalBytesSent | 538768 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 541664 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 550352 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 553248 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 557056 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | App went into background.

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 564832 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 567728 of 1231286 | queueIndex: 3

2016-02-21 05:53:03 +0000 | in networking | totalBytesSent | 582208 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 585104 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 589824 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 621680 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | App came into foreground. 

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 622592 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 655360 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 688128 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 720896 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 753664 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 786432 of 1231286 | queueIndex: 3

2016-02-21 05:53:14 +0000 | in networking | totalBytesSent | 819200 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 851968 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 884736 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 887632 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 893424 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 917504 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 939224 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 950272 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 970544 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 983040 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1015808 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1048576 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1081344 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1114112 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1146880 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1179648 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1212416 of 1231286 | queueIndex: 3

2016-02-21 05:53:15 +0000 | in networking | totalBytesSent | 1231286 of 1231286 | queueIndex: 3

2016-02-21 05:53:16 +0000 | in networking | didCompleteWithError | queueindex: 3

bkgd debug - processing next file

2016-02-21 05:53:16 +0000 | bkgd debug - about to start upload task | queueIndex: 4

2016-02-21 05:53:16 +0000 | in networking | totalBytesSent | 32768 of 1278039 | queueIndex: 4

2016-02-21 05:53:16 +0000 | in networking | totalBytesSent | 65536 of 1278039 | queueIndex: 4

2016-02-21 05:53:16 +0000 | in networking | totalBytesSent | 98304 of 1278039 | queueIndex: 4

2016-02-21 05:53:16 +0000 | in networking | totalBytesSent | 131072 of 1278039 | queueIndex: 4

乐于尝试任何事情.谢谢!

Happy to try anything at this point. Thanks!

在操作系统执行后台上传application:handleEventsForBackgroundURLSession:completionHandler:时观察到.在无法正常工作的情况下,永远不会进行回叫.

Observed when background upload does work application:handleEventsForBackgroundURLSession:completionHandler: is called by the OS. Invariably that call back is never made when it doesn't work.

我不确定后台上传的先决条件是否是操作系统必须首先杀死该应用程序.如果是这样,在什么条件下会发生?我们可以提示吗?

I'm not sure if a prerequisite for background upload is the OS must kill the App first. If so, under what conditions does that happen? And can we prompt it?

50次中有49次提到,操作系统会将应用程序保留在后台,并暂停上传.

As mentioned 49 times out of 50, OS will keep the app in the background, and upload is halted.

推荐答案

有一件事想弄清楚,因为Apple不允许,您不能长时间在后台运行任何任务.苹果只有在特殊情况下才考虑使用它.最好在在iOS中运行后台服务

There is one thing wanted to make it clear is you cannot keep running in background any task for long, as Apple doesn't allows you. In only special cases Apple consider it. Best explained in Running background services in iOS

现在,您的实现又回到了问题所在,因为它仅在后台运行,用于上传任务,该任务在应用程序处于活动状态且任务仍未完成时启动.这就是您在50次尝试中看到任务在后台运行的原因.

Now coming back to your question issue with your implementation is it will only work in background for upload task which are initiated when app was active and still task is not complete. Thats the reason for 1 in 50 attempts you see task is working in background.

现在要解决您的问题,您必须立即启动全部/批量上传,以防万一应用程序在后台运行,仍然可以上传文件.这份精妙的教程解释了与背景传输有关的不同情况.

Now to solve your issue you have to initiate all/bunch of upload at once so that incase if app goes in background still your app will be able upload files. This briliant tutorial explain different cases related to Background Transfer.

您还可以尝试 AFNetworking 分段上传请求.

Also you can try out AFNetworking multipart request for upload.

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];

这篇关于NSURLSession后台上传不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆