下载多个文件使用NSURLSession并在后台状态下保持其进度

时间:2014-03-03 09:59:14

标签: ios ios7 nsurlconnection nsurlsession

我已经使用NSURLSession下载单个文件,它工作正常,现在我必须在后台下载三个文件,还必须管理他们在UIProgress中的进度。我的单个下载代码如下:

- (IBAction)startBackground:(id)sender 
{
    // Image CreativeCommons courtesy of flickr.com/charliematters
    NSString *url = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];
    [self setDownloadButtonsAsEnabled:NO];
    self.imageView.hidden = YES;
    // Start the download
    [self.backgroundTask resume];
}

- (NSURLSession *)backgroundSession
{
    static NSURLSession *backgroundSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.shinobicontrols.BackgroundDownload.BackgroundSession"];
        backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    });
    return backgroundSession;
}

#pragma mark - NSURLSessionDownloadDelegate methods
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    double currentProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;
    dispatch_async(dispatch_get_main_queue(), ^{
        self.progressIndicator.hidden = NO;
        self.progressIndicator.progress = currentProgress;
    });
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    // Leave this for now
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    // We've successfully finished the download. Let's save the file
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSURL *documentsDirectory = URLs[0];

    NSURL *destinationPath = [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]];
    NSError *error;

    // Make sure we overwrite anything that's already there
    [fileManager removeItemAtURL:destinationPath error:NULL];
    BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error];

    if (success)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage *image = [UIImage imageWithContentsOfFile:[destinationPath path]];
            self.imageView.image = image;
            self.imageView.contentMode = UIViewContentModeScaleAspectFill;
            self.imageView.hidden = NO;
        });
    }
    else
    {
        NSLog(@"Couldn't copy the downloaded file");
    }

    if(downloadTask == cancellableTask) {
        cancellableTask = nil;
    } else if (downloadTask == self.resumableTask) {
        self.resumableTask = nil;
        partialDownload = nil;
    } else if (session == self.backgroundSession) {
        self.backgroundTask = nil;
        // Get hold of the app delegate
        SCAppDelegate *appDelegate = (SCAppDelegate *)[[UIApplication sharedApplication] delegate];
        if(appDelegate.backgroundURLSessionCompletionHandler) {
            // Need to copy the completion handler
            void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;
            appDelegate.backgroundURLSessionCompletionHandler = nil;
            handler();
        }
    }

 }

 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
 {
     dispatch_async(dispatch_get_main_queue(), ^{
        self.progressIndicator.hidden = YES;
        [self setDownloadButtonsAsEnabled:YES];
     });
 }

1 个答案:

答案 0 :(得分:5)

你可以让多个NSURLSessionDownloadTask使用相同的NSSession,并且每个NSSession在同一个mainQueue上一个接一个地执行。

成功下载后,他们致电:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location

如果您下载100 mB的MP4和10KB的图片,那么他们将以不同的顺序返回此方法。

因此,要跟踪此

中返回的会话和downloadTask

您需要在调用Web服务之前设置字符串标识符

要区分/跟踪 NSURLSessions ,请设置

session.configuration.identifier

区分 NSURLSessionDownloadTask 使用

downloadTask_.taskDescription

downloadTask_.taskDescription =  [NSString stringWithFormat:@"%@",urlSessionConfigurationBACKGROUND_.identifier];

例如,在我的项目中,我正在下载一些用户喜爱的视频及其缩略图。

每个项目都有一个id,例如1234567 所以我需要为每个最喜欢的

打两个电话

所以我创建了两个标识符

"1234567_VIDEO"
"1234567_IMAGE"

然后调用两个ws调用并传入session.configuration.identifier

中的标识符
http://my.site/getvideo/1234567
"1234567_VIDEO"

http://my.site1/getimage/1234567
"1234567_IMAGE"
iOS7将在后台下载项目,app可以回去睡觉 完成后调用

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
然后我得到

session.configuration.identifier
"1234567_IMAGE"

将其拆分并检查值

1234567_IMAGE
"1234567"
"_IMAGE"   > item at location is a MP4 so save as /Documents/1234567.mp4
"_VIDEO"   > item at location is a jpg so save as /Documents/1234567.jpg

如果您有3个网址可以调用,则每个NSSession可以有一个NSURLSessionDownloadTask

file 1 - NSSession1 > NSURLSessionDownloadTask1
file 2 - NSSession2 > NSURLSessionDownloadTask2
file 3 - NSSession3 > NSURLSessionDownloadTask3

当应用程序位于前台时,这似乎工作正常。 但是当使用BACKGROUND TRANSFER和BACKGROUND FETCH时我遇到了问题。 第一个NSSession> NSURLSessionDownloadTask1将返回,然后不会调用其他任何一个。

在一个NSSession1中有多个NSURLSessionDownloadTask更安全

file 1 - NSSession1 > NSURLSessionDownloadTask1
file 2 -            > NSURLSessionDownloadTask2
file 3 -            > NSURLSessionDownloadTask3

这样做时要小心 调用NSSession finishTasksAndInvalidate not invalidateAndCancel

  //[session invalidateAndCancel];
   [session finishTasksAndInvalidate];

invalidateAndCancel将停止会话而不完成其他下载任务