NSURLSession 后台会话中的 NSURLSessionDownloadTask 给出错误

Posted

技术标签:

【中文标题】NSURLSession 后台会话中的 NSURLSessionDownloadTask 给出错误【英文标题】:NSURLSessionDownloadTask in NSURLSession Background Session gives error 【发布时间】:2016-11-07 06:44:34 【问题描述】:

我正在学习 NSURLSession 来制作一个自定义网络类,并偶然发现了一个非常不寻常的错误。

我的目标很简单,一旦完成,我想将 URL 的响应写入文件中。所以我创建了一个下载任务并将其分配给一个defaultSessionConfiguration。在将委托分配给配置和不将委托分配给配置的两种情况下(在这种情况下完成处理程序工作)都有效。

现在我转到 backgroundSessionConfigurationWithIdentifier。后台会话不支持 blocks ,因此必须调用委托。

每次都会出错。 错误如下所示

Printing description of error:
Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo=NSErrorFailingURLKey=http://__________________, NSErrorFailingURLStringKey=http://__________________, NSLocalizedDescription=unknown error

我想我一定是后台配置写错了,所以我通过创建一个演示下载任务来下载图像并将这个任务添加到这个后台会话中来测试它。 这次成功了

有效的代码如下:

+ (CustomNetwork *)sharedNetworkObject

     if(!netWorkObj)
     
          netWorkObj = [[CustomNetwork alloc] init];

          //[netWorkObj prepareDataSession];

          //[netWorkObj prepareBackgroundSession];
     
     return netWorkObj;

//
+ (NSOperationQueue *)responseQueue

     if(!queue)
     
          queue = [[NSOperationQueue alloc] init];
     
     return queue;


- (void)prepareDataSession

     if(!dataSession)
     
          NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
          configuration.HTTPMaximumConnectionsPerHost = 5; // This means 1 session can hold to 5 connections
          configuration.timeoutIntervalForRequest = CONNECTION_TIME_OUT;
          configuration.timeoutIntervalForResource = CONNECTION_TIME_OUT;
          dataSession = [NSURLSession sessionWithConfiguration:configuration
                                                      delegate:nil //[CustomNetwork sharedNetworkObject]
                                                 delegateQueue:nil];
     


- (void)prepareBackgroundSession

     if(!backgroundSession)
     
          NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"backgroundSession"];
          configuration.HTTPMaximumConnectionsPerHost = 5; // This means 1 session can hold to 5 connections
          configuration.timeoutIntervalForRequest = CONNECTION_TIME_OUT;
          configuration.timeoutIntervalForResource = CONNECTION_TIME_OUT;
          configuration.discretionary = NO;// For optimizing
          backgroundSession = [NSURLSession sessionWithConfiguration:configuration
                                                      delegate:[CustomNetwork sharedNetworkObject]
                                                 delegateQueue:nil];
     


+ (void)demoBackGroundSessionWorks

     NSURL * url = [NSURL URLWithString:@"https://www.wallpapereast.com/static/images/wallpaper-photos-42.jpg"];//@"http://www.hdwallpapersinn.com/wp-content/uploads/2012/09/HD-Wallpaper-1920x1080.jpg"];
     //NSURLSessionConfiguration * backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:@"backgroundtask1"];

     [[CustomNetwork sharedNetworkObject] prepareBackgroundSession];

     NSURLSessionDownloadTask * downloadTask =[backgroundSession downloadTaskWithURL:url];
     [downloadTask resume];


给出失败的代码:

    +(void)createDownloadConnectionWithUrl:(NSString *)aUrl
                                                    operationKey:(NSString *)operationKey
                                                       jsonParam:(NSString *)jsonString
                                                      HTTPMethod:(NSString *)method
                                                  startImmediate:(BOOL)startImmediate
                                                    downloadPath:(NSString *)downloadPath

    
         if([[aUrl trimmedString] length] && [self isValidMethod:method] && [[downloadPath trimmedString] length])
         
              CustomURLRequest *dataRequest = [CustomNetwork requestURLWithString:aUrl];
              dataRequest.downloadDestinationPath = downloadPath;

              [self prepareTheDataRequest:&dataRequest
                           WithParameters:&jsonString
                                ForMethod:&method
                          andOpertaionKey:operationKey];


              // Remove any file if present in the path
              if([[NSFileManager defaultManager] fileExistsAtPath:downloadPath])
              
                   [[NSFileManager defaultManager] removeItemAtPath:downloadPath error:nil];
              

              // PS : if you are using BackGroundSession, completion block wont work and will give to a NSGenericException. Reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'

              [[CustomNetwork sharedNetworkObject] prepareBackgroundSession];
              NSURLSessionDownloadTask *downloadTask =  [backgroundSession downloadTaskWithRequest:dataRequest];

if(startImmediate)
          
               [downloadTask resume];
          

prepare 方法将标头添加到请求中。

如果我使用 dataSession 而不是 backgroundSession

,同样的代码会失败

问:同样的请求在 defaultConfiguration 中有效,但在 backgroundSession 中失败。我是否遗漏了什么,或者与 Apple Docs 的“仅支持上传和下载任务(无数据任务)”学说有关。

【问题讨论】:

您不能将data taskbackgroundsessionconfiguration 一起使用,因为您必须将fileurl 提供给upload fromdownload to。显示您的代码,以便有人可以提供帮助! 我已经添加了一些代码,但仍然不是很清楚。如果我想下载数据响应,BackgroundSessions 将不起作用,但如果我想下载图像或文件,BackgroundSessions 将起作用? 如果您使用默认会话配置而不是后台调用createDownloadConnectionWithUrl 会发生什么? 当我使用 defaultConfiguration 时它可以工作。调用委托方法 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location 并在“location”中创建 *.tmp 文件 refer this so post, it may help! 【参考方案1】:

后台会话不关心他们正在下载什么(文件或任意数据或其他)。您只需要使用下载任务或上传任务,而不是数据任务。但你正在这样做。

我的猜测是,没有看到其余代码,CustomURLRequestNSURLRequest 的子类。不幸的是,NSURLSession 不支持NSURLRequest 的子类,尤其是实际直接添加属性的子类。在各种版本的 ios 和 OS X 中,您会遇到的错误范围从严重到完全彻底破坏。我什至见过您从一个请求中获取自定义属性值以及从完全不同的请求中获取 URL 和标头的情况。

您应该在NSURLRequest 上创建自定义类别,而不是子类化。在该类别中,创建 getter 和 setter 方法。

使用 [NSURLProtocol propertyForKey:inRequest:] 作为您的吸气剂。 使用 [NSURLProtocol setProperty:forKey:inRequest:] 作为您的二传手。

重要提示:使用这些方法存储在NSURLRequest 中的所有对象必须是属性列表可序列化的。如果您需要将自定义类的实例与NSURLRequest 关联,则应将它们存储在单独的NSDictionary 中,并使用任意字典键(例如调用[[NSUUID UUID] UUIDString] 的结果),您可以安全地将其存储在NSURLRequest 对象及以后用于从外部字典中获取自定义类。

【讨论】:

CustomURLRequest 确实是 NSURLRequest 的子类。 还有一件事,我想问。如何为不同的 NSURLrequests 设置不同的 timeOut ?唯一的方法是创建一个具有上述超时的会话吗? 您可以在请求本身上设置timeoutInterval(通过可变请求上的属性或通过在创建请求时传入超时值),它会覆盖timeoutIntervalForRequest。设置timeoutIntervalForResource 的唯一方法是创建一个新会话,但您可能不想设置它。 (默认值为 7 天。)

以上是关于NSURLSession 后台会话中的 NSURLSessionDownloadTask 给出错误的主要内容,如果未能解决你的问题,请参考以下文章

应用程序终止时的 NSURLSession 后台会话回调

iOS NSURLSession 实现网络请求-文件下载-上传-后台下载

iOS NSURLSession 实现网络请求-文件下载-上传-后台下载

NSURLCache 用于 NSURLSession 后台任务

NSURLSession 导致 0B 图像上传到 GCS

在 iOS8 中使用 NSUrlSession 使用流请求进行后台上传