处理NSURLSessionDownloadTask失败

8

最初,我认为如果NSURLSessionDownloadTask成功完成,会调用URLSession:downloadTask:didFinishDownloadingToURL:方法;如果由于某些原因失败,则会调用URLSession:task:didCompleteWithError:方法。

在模拟器上,它按预期工作(每个下载任务只调用一个方法),但在设备上却不是这样的:在失败的情况下,这两种方法都会被调用,其中URLSession:downloadTask:didFinishDownloadingToURL:是第一个被调用的方法。(并且这两种方法都使用相同的任务参数)

我是否遗漏了什么?


我注意到在DidFinishDownloadingToURL被调用的同时,didCompleteWithError也会被调用。这给我们带来了很大的问题。你是如何解决这个问题的? - RunLoop
在这种情况下,位置可能为空。你能检查一下吗? - AsifHabib
5个回答

3
我找到了解决这个问题的方法:
要在响应头中获取状态码,您必须首先启动一个NSURLSessionDataTask。
这将调用以下委托方法URLSession:dataTask:didReceiveResponse:completionHandler:。
在此方法中,您可以首先检查NSURLResponse参数的状态码(通过将其转换为NSHTTPURLResponse),最后使用NSURLSessionResponseBecomeDownload调用完成处理程序,将您的dataTask转换为downloadTask(它将表现出您从NSURLSessionDownloadTask所期望的行为),或者使用NSURLSessionResponseCancel避免下载一些您不需要的数据(例如,如果响应的状态码是404)。
此外,如果您需要对转换后的NSURLSessionDownloadTask执行某些操作(例如将其存储在数组或字典中,或用新对象替换数据任务),可以在URLSession:dataTask:didBecomeDownloadTask:中完成。
希望这能帮助到某些人!

2
如果你正在进行HTTP请求,你可以将任务的响应强制转换为NSHTTPURLResponse来获取HTTP状态码:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"响应是 %d\n" ((NSHTTPURLResponse)downloadTask.response).statusCode); NSLog(@"错误是 %@\n", [downloadTask.error localizedDescription]); }
苹果公司的逻辑是当你收到404时,错误仍将为空。

2
根据苹果公司在NSURLSessionDownloadDelegate文档中的说明,这是标准行为。
/* Sent when a download task that has completed a download.  The delegate should 
 * copy or move the file at the given location to a new location as it will be
 * removed when the delegate message returns. URLSession:task:didCompleteWithError: 
 * will still be called. */

1
使用完成块代替委托:
NSURLSessionDownloadTask *mySessionDownloadTask = [myURLSession downloadTaskWithRequest:myRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
   dispatch_async(dispatch_get_main_queue(), ^{
      if(!error)
       {
           // Finish loading
       }
      else
       {
              // Handle error
        });
}];

注意:如果你没有获取到主队列,任何与用户界面相关的更新都会被延迟,从而导致意外的行为。

1
我希望当我的应用程序进入后台模式时,用户的下载可以完成,因此我不能使用委托而是要使用完成块(因为我正在使用后台会话)。 - dariaa
我明白你的意思。那么,使用全局BOOL来判断是否已处理响应怎么样? - Thanh-Nhon Nguyen
当然,肯定有一些变通方法,但我想要更深入地了解。这是预期的行为还是应该报告的错误等。顺便说一下,在这里使用BOOL标志不是最好的选择,因为在URLSession:downloadTask:didFinishDownloadingToURL:(首先调用)中,通常会将文件复制到文档目录以进行进一步处理。在那个时候知道下载是否成功完成或出现错误会很好。 - dariaa

-1

NSURLSessionDownloadTaskNSURLSessionTask 的子类,它有一个 error 属性。在你尝试复制文件之前,请在你的 URLSession:downloadTask:didFinishDownloadingToURL: 代理方法中检查一下。


1
除非是客户端错误,否则错误属性将保持为nil。 - Julius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接