NSURLSession后台上传无效

时间:2016-02-21 09:34:29

标签: ios objective-c background-process

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

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

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

  1. 我会点击用户界面上的“上传”按钮
  2. 文件将开始在我的服务器上逐一显示
  3. 点击iPhone上的“主页”按钮,让应用程序进入后台
  4. 文件将继续上传到服务器,直到全部完成
  5. 最近几天,我一直在网络模块之外进行一些重构。几天前我再次尝试上传时,只要按下“主页”按钮,上面的步骤(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)有一种方便的方法叫做实际上传。每个会话只有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委托中,我检查是否所有文件都已上传,如果没有,请转到下一个文件 - 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次,操作系统会将应用程序保留在后台,并暂停上传。

4 个答案:

答案 0 :(得分:2)

有一件事想说清楚,你不能长时间在后台运行任何任务,因为Apple不允许你。只有在特殊情况下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];

答案 1 :(得分:1)

我的一个视频应用中也有同样的问题。这个问题发生在2月14日之后。我挖了很多,发现这发生在苹果改变了他们的全球证书之后。请检查此https://developer.apple.com/support/certificates/expiration/。 解决方案是首先从钥匙串访问中撤消现有证书,然后添加新的开发/分发证书,新的应用程序ID和配置文件。它肯定会奏效。

答案 2 :(得分:1)

我认为问题出在上传部分。您在完成上一个任务后启动下一个上载任务,而不是创建所有上载任务一次。在您的appDelegate中,将application:handleEventsForBackgroundURLSession:completionHandler:的完成处理程序存储为属性。使用NSURLSessionDelegate的{​​{1}} URLSessionDidFinishEventsForBackgroundURLSession:委托方法调用完成块。在拨打电话之前,请确保完成所有上传任务。本教程http://www.appcoda.com/background-transfer-service-ios7/很好地解释了下载任务。可能是您可以为上传任务应用相同的规则。

答案 3 :(得分:1)

我遇到了类似的问题,文件正在上传。它在后台停了。

在审核文档和类似答案后花了几天时间。This solution worked for me

  1. UIBackgroundTaskIdentifier是非常有用的重要财产之一。 在app delegate类中创建此内容,并将其初始化为UIBackgroundTaskInvalid中的didFinishLaunchingWithOptions
  2. -

    @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;
    }
    
    1. 应用程序正在将状态从Foreground转换为Background,反之亦然。我们需要确保在其相关的委托方法中检查此属性。
    2.   

      beginBackgroundTaskWithExpirationHandler   这对于在背景中实际启动后台任务非常重要。

      -

      - (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;
          }
      }
      

      您可以调试实际设备中的代码,并在适当的网络条件下进行检查。

      1. 此外,您可以添加以下委托方法来跟踪bytesSent。
      2. -

        - (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);
        
        
        }
        

        我希望它对某人有帮助。