NSURLSession后台上传无法正常工作

sno*_*und 8 objective-c background-process ios

我正在尝试将一系列文件从iPhone上传到服务器,目的是即使应用程序处于后台或暂停,也会上传这些文件.

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

奇怪的是它在两周前完全正常工作.如:

  1. 我会点击UI上的"上传"按钮
  2. 文件将开始在我的服务器上逐一显示
  3. 我点击iPhone上的"主页"按钮,让应用程序进入后台
  4. 文件将继续上传到服务器,直到它们全部完成

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

这就好像后台上传甚至不起作用(对于一个文件,更不用说多个文件).

我已多次运行代码,发现它的工作量约为1/50.但其他49次没有.我还检查了以前使用的代码版本(服务器+ iOS),它不再有效 - 或者说,工作很少变化(1/50)

多次完成背景传输和URL会话生命周期规则,以确保我遵守Apple建议的指导方针,我正在绞尽脑汁想知道什么破坏了,这让人难以理解这是多么不合逻辑 - 我怀疑它是代码实现以外的东西.

所以任何帮助都表示赞赏......

履行

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

    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];
Run Code Online (Sandbox Code Playgroud)

2)有一种方便的方法叫做实际上传.每个会话只有1个上传任务:

    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];
Run Code Online (Sandbox Code Playgroud)

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

注意,我也等了比下面输出中显示的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
Run Code Online (Sandbox Code Playgroud)

很高兴在这一点上尝试任何事情.谢谢!

编辑#1

application:handleEventsForBackgroundURLSession:completionHandler:操作系统调用后台上传工作时观察到的.当它不起作用时,永远不会发出回叫.

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

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

小智 3

有一点需要明确的是,你不能在后台长时间运行任何任务,因为苹果不允许你这样做。只有在特殊情况下,苹果才会考虑它。在 iOS 中运行后台服务中得到了最好的解释

现在回到您的实施问题,它只会在后台工作,上传任务是在应用程序处于活动状态且任务尚未完成时启动的。这就是您看到任务在后台运行的 50 次尝试中有 1 次的原因。

现在要解决您的问题,您必须立即启动所有/一堆上传,以便万一应用程序进入后台,您的应用程序仍然能够上传文件。这个精彩的教程解释了与后台传输相关的不同案例。

您也可以尝试AFNetworking分段请求上传。

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];
Run Code Online (Sandbox Code Playgroud)