Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/112.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios NSURLSession后台上载不工作_Ios_Objective C_Background Process - Fatal编程技术网

Ios NSURLSession后台上载不工作

Ios NSURLSession后台上载不工作,ios,objective-c,background-process,Ios,Objective C,Background Process,我正试图将一系列文件从iPhone上传到服务器,目的是让这些文件的上传即使在应用程序处于后台或挂起时也能进行 我正在使用nsursession及其系列API提供的后台传输 奇怪的是,它在两周前就完全工作了。例如: 我会点击UI上的“上传”按钮 文件将开始在我的服务器上逐个显示 我点击iPhone上的“主页”按钮,让应用程序进入后台 文件将继续上载到服务器,直到全部完成 过去几天,我一直在网络模块之外进行重构。几天前,当我再次尝试上传时,只要我点击上面第(3)步的“主页”按钮。当我再次进入应用程序

我正试图将一系列文件从iPhone上传到服务器,目的是让这些文件的上传即使在应用程序处于后台或挂起时也能进行

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

奇怪的是,它在两周前就完全工作了。例如:

  • 我会点击UI上的“上传”按钮
  • 文件将开始在我的服务器上逐个显示
  • 我点击iPhone上的“主页”按钮,让应用程序进入后台
  • 文件将继续上载到服务器,直到全部完成
  • 过去几天,我一直在网络模块之外进行重构。几天前,当我再次尝试上传时,只要我点击上面第(3)步的“主页”按钮。当我再次进入应用程序时,文件上传将停止。上传将继续

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

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

    在经历了多次后台传输和确保遵守苹果建议的指导原则之后,我绞尽脑汁想到底是什么坏了,这让人难以置信,这是多么不合逻辑-我怀疑这不是代码实现。

    所以任何帮助都是非常感谢的

    实施 1) 在我的网络类(单例)的
    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);
    
    
    }