ios - NSURLSession 后台上传不工作

标签 ios objective-c background-process

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

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

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

  1. 我会点击用户界面上的“上传”按钮
  2. 文件将开始一个接一个地出现在我的服务器上
  3. 我点击 iPhone 上的“主页”按钮,让应用程序进入后台
  4. 文件将继续上传到服务器,直到全部完成

最近几天,我一直在网络模块之外进行一些重构。几天前,当我再次尝试上传时,我一点击“主页”按钮,即上面的第 (3) 步。当我再次进入该应用程序时,文件上传将停止。上传将恢复。

好像后台上传根本不起作用(对于一个文件,更不用说多个文件了)。

我已经多次运行该代码,发现它有大约 1/50 次有效。但其他 49 次却没有。我还检查了曾经工作的代码版本(服务器 + iOS),但它不再工作 - 或者更确切地说,工作很少变化(1/50)

经历了rules用于后台传输和Lifecycle of URL Session多次确保我遵守 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];

2) 有一个方便的方法调用来进行实际的上传。每个 session 只有 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];

3) 在 didCompleteWithError 委托(delegate)中,我检查是否所有文件都已上传,如果没有,则移至下一个文件 - 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];
    }
}

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

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

编辑#1

在后台上传正常工作时观察 application:handleEventsForBackgroundURLSession:completionHandler: 由操作系统调用。当它不起作用时,总是不会进行回调。

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

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

最佳答案

有一件事需要明确说明,您不能长时间在后台运行任何任务,因为 Apple 不允许。只有在特殊情况下,苹果才会考虑。最佳解释在 Running background services in iOS

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

现在要解决您的问题,您必须立即启动所有/批量上传,这样如果应用程序进入后台,您的应用程序仍然可以上传文件。这个辉煌tutorial解释与后台传输相关的不同案例。

你也可以试试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];

关于ios - NSURLSession 后台上传不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35534486/

相关文章:

java - React-Native 超时中的 headless (headless)任务 (Android)

ios - 如何解决 'init(proximityUUID:identifier:)' 在 iOS 13.0 for iBeacon 中被弃用

ios - 什么时候使用@atomic?

android - 从移动浏览器的初始视口(viewport)中排除 DIV

iphone - 推特发帖错误

php - 在php后台运行C exec

ios - ISO 8601 日期的正确日期格式

ios - 应用商店内产品 View ,不适用于 IOS 5,如何测试?

ios - UIImage imageNamed : always returns image at 1x

ios - iOS 中的后台下载速度