使用NSURLSessionDownloadTask逐个下载文件

时间:2014-10-10 10:31:14

标签: ios nsurlsession nsurlsessiondownloadtask

在我的应用程序中,我从Web服务获取JSON。这个JSON包含我想下载的几个文件的URL。

我想逐个下载每个文件(等待第一次下载完成,直到第二次下载完成,依此类推)使用NSURLSessionDownloadTask。我还想跟踪写入的总字节数,以便我可以更新UI。

提前多多感谢!

1 个答案:

答案 0 :(得分:2)

NSURLSessionDownloadTask's你知道与NSOperationQueues不太一致,不像NSURLConnection(它可以封装在NSOperation中)。

一个选项是将所有网址添加到数组中,然后在任务的completionHandler中,只需将下一个项目排队。

因此,您可以在循环中创建任务,在每个任务完成处理程序中调用progressBlock,将任务存储在数组中,并将每个任务完成处理程序中的下一个任务排队:

- (void)addRequestsWithURLs:(NSArray *)urls
              progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations, NSURLSessionDownloadTask *task,NSURL *location, NSURLResponse *response, NSError *error))progressBlock {
    __block NSUInteger numberOfFinishedOperations = 0;
    NSUInteger totalNumberOfOperations = [urls count];
    for (NSString *url in urls) {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
        __block NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request
                                                                completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
                                                                   //downloadFileSomewhere

                                                                    ++numberOfFinishedOperations;
                                                                        if (progressBlock) {
                                                                            progressBlock(numberOfFinishedOperations, totalNumberOfOperations,task,destination != nil ? [NSURL fileURLWithPath:destination] : nil,response,error);

                                                                        }
                                                                    //queueNext
                                                                    [self processCompletedTask:task];
                                                                }];
        //stores an array of NSURLSessionTasks
        [self.tasksWaitingToBeQueued addObject:task];
    }

}

- (void)processCompletedTask:(NSURLSessionTask *)completedTask {
    //clean up and queue next one
    [self.tasksWaitingToBeQueued removeObject:completedTask];        
    nextTask = [self.tasksWaitingToBeQueued firstObject];
    if (nextTask) {
        [nextTask resume];
    }
}

注意

在这个例子中,我表示进度是完成的任务数而不是字节数,这是推荐的方法(它也更简单)。要使用字节指示进度,您需要知道要预先下载的总字节数(因为您要显示进度条),还要实现NSURLSession委托并监视每个任务的进度,捕获下载的字节并更新块。如果您的服务器没有告诉您总字节数,那么您可能需要为每个资源执行HEAD请求并聚合大小。就个人而言,这个解决方案是复杂的,可以通过将进度指示为下载的文件数来简单地解决。

要实现这一目标可能如下所示:

- (void)URLSession:(NSURLSession *)session 
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
 totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    self.totalBytesWritten += totalBytesWritten;
    NSUInteger totalProgressSoFar = self.totalBytesWritten;
    NSUInteger totalExpectedBytes = self.totalExpectedBytes;
    //you would need to capture some progress block locally - beware of retain cycles
    self.progressBlock(totalProgressSoFar/totalExpectedBytes)
}

完成后,您应将progressBlock设置为nil为prevent any retain cycles