ios7 - iOS 7 NSURLSession 在后台下载多个文件

标签 ios7 background fetch nsurlsession

我想使用 NSUrlSession 下载文件列表。

我有一个用于计算成功下载的变量 @property (nonatomic) int downloadsSuccessfulCounter; .在下载文件时,我禁用了 Download Button .当计数器等于下载列表大小时,我再次启用按钮并将计数器设置为 0。我在方法中执行此操作:

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

...

    [[NSOperationQueue mainQueue] addOperationWithBlock:^ {

        downloadsSuccessfulCounter++;

        if(downloadsSuccessfulCounter == self.downloadList.count) {
            NSLog(@"All downloads finished");

            [self.syncButton setEnabled:YES];

             downloadsSuccessfulCounter = 0;
        }
    }];

}

一切正常,但是当我再次打开 ViewController 时,我收到消息 A background URLSession with identifier com.myApp already exists! .计数器未设置为 0,并且 UI 元素(UIButtons、UILabels)没有响应。

我想问题是因为 NSURLSession 仍然打开,但我不确定它是如何工作的。

我已经尝试了所有教程,但其中 99% 仅用于下载 1 个文件,不超过 1...
有任何想法吗?

这是我的代码:
...    
@property (nonatomic, strong) NSURLSession *session;
...

    - (void)viewDidLoad {
        [super viewDidLoad];

        appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        self.downloadList = [[NSMutableArray alloc] init];

        NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.myApp"];
        sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
        self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
}

当我按下 Download Button我称这种方法(
我有一个 Downloadable包含 NSURLSessionDownloadTask 的对象):
-(void)startDownload {

    for (int i=0; i<[self.downloadList count]; i++) {
        Downloadable *d = [self.downloadList objectAtIndex:i];

        if (!d.isDownloading) {
            if (d.taskIdentifier == -1) {
                d.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:d.downloadSource]];

            }else {
                d.downloadTask = [self.session downloadTaskWithResumeData:fdi.taskResumeData];
            }

            d.taskIdentifier = d.downloadTask.taskIdentifier;
            [d.downloadTask resume];
            d.isDownloading = YES;
        }
    }
}

当应用程序处于后台时:
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

        if ([downloadTasks count] == 0) {
            if (appDelegate.backgroundTransferCompletionHandler != nil) {

                void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;

                appDelegate.backgroundTransferCompletionHandler = nil;

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{               
                    completionHandler();

                    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                    localNotification.alertBody = @"All files downloaded";
                    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];

                }];
            }
        }
    }];
}

最佳答案

所以,正如我在评论中提到的,问题是每个文件都需要一个唯一的 NSURLSession,每个 NSURLSession 都需要一个具有唯一标识符的 NSURLSessionConfiguration。

我认为你很亲近——而且在某些方面可能比我更合适……
您只需要创建一个结构来将唯一 ID 传递到唯一配置中,以填充唯一 session (比如 10 倍快)。

这是我所做的:

/*
* 检索要下载的文件列表
* 还使用该列表的大小来实例化项目
* 在我的情况下,我加载了一个字符返回的文本文件,其中包含我要下载的文件的名称
*/

- (void) getMediaList {

    NSString *list = @"http://myserver/media_list.txt";
    NSURLSession *session = [NSURLSession sharedSession]; // <-- BASIC session
    [[session dataTaskWithURL:[NSURL URLWithString:list]
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                NSString *stringFromData = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];

                // Populate Arrays
                REMOTE_MEDIA_FILE_PATHS = [stringFromData componentsSeparatedByString:@"\n"];
                [self instantiateURLSessions:[REMOTE_MEDIA_FILE_PATHS count]]; 


                // Start First File
                [self getFile:[REMOTE_MEDIA_FILE_PATHS objectAtIndex:downloadCounter]:downloadCounter]; // this variable is 0 at the start
            }]
     resume];
}

/*
* 这会将配置和 session 数组设置为适当的大小
* 它还给每个人一个唯一的 ID
*/
- (void) instantiateURLSessions : (int) size {

    NSMutableArray *configurations = [NSMutableArray array];
    NSMutableArray *sessions = [NSMutableArray array];

    for (int i = 0; i < size; i++) {
        NSString *index = [NSString stringWithFormat:@"%i", i];
        NSString *UniqueIdentifier = @"MyAppBackgroundSessionIdentifier_";
        UniqueIdentifier = [UniqueIdentifier stringByAppendingString:index];

        [configurations addObject: [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:UniqueIdentifier]];
        [sessions addObject:[NSURLSession sessionWithConfiguration: [configurations objectAtIndex:i]  delegate: self delegateQueue: [NSOperationQueue mainQueue]]];
    }

    NSURL_BACKGROUND_CONFIGURATIONS = [NSArray arrayWithArray:configurations];
    NSURL_BACKGROUND_SESSIONS = [NSArray arrayWithArray:sessions];
}

/*
* 这会根据数组的索引为每个文件设置下载任务
* 它还连接到实际文件的路径
*/
- (void) getFile : (NSString*) file :(int) index {
    NSString *fullPathToFile = REMOTE_MEDIA_PATH; // Path To Server With Files
    fullPathToFile = [fullPathToFile stringByAppendingString:file];

    NSURL *url = [NSURL URLWithString:fullPathToFile];
    NSURLSessionDownloadTask *downloadTask = [[NSURL_BACKGROUND_SESSIONS objectAtIndex:index ] downloadTaskWithURL: url];
    [downloadTask resume];

}

/*
* 最后,在我的委托(delegate)方法中,下载完成后(在文件从临时数据中移动后),我检查是否完成,如果没有使用更新的索引计数器再次调用 getFiles 方法
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{

    // Get the documents directory URL
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:LOCAL_MEDIA_PATH];
    NSURL *customDirectory = [NSURL fileURLWithPath:dataPath];

    // Get the file name and create a destination URL
    NSString *sendingFileName = [downloadTask.originalRequest.URL lastPathComponent];
    NSURL *destinationUrl = [customDirectory URLByAppendingPathComponent:sendingFileName];

    // Move the file
    NSError *error = nil;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager moveItemAtURL:location toURL:destinationUrl error: &error]) {

        // List
        [self listCustomDirectory];

        if(downloadCounter < [REMOTE_MEDIA_FILE_PATHS count] -1) {
            // Increment Counter
            downloadCounter++;

            // Start Next File
            [self getFile:[REMOTE_MEDIA_FILE_PATHS objectAtIndex:downloadCounter]:downloadCounter];
        }
        else {
            // FINISH YOUR OPERATION / NOTIFY USER / ETC
        }
    }
    else {
        NSLog(@"Damn. Error %@", error);
        // Do Something Intelligent Here
    }  
}

关于ios7 - iOS 7 NSURLSession 在后台下载多个文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25762034/

相关文章:

android - 仅当应用程序在 android pie 中关闭时,广播接收器才起作用

ios - 当用户按下按钮时显示 SplitViewController?

ios - Xcode 7 GM 缺少 iOS 7 模拟器

css - css div 标签中的背景图片

reporting-services - 折线图中的条件背景颜色

mysql - 无法从 PHP 和 MySQL 获取印地语的 Unicode 数据

ios - 如何将 JSON 数据提取到 2 列中的原型(prototype)单元格

javascript - 捆绑的 JS 库,例如angular, jQuery, lodash 在 eval 抛出 "require is not defined"之后

ios - iPhone 上的谷歌翻译 API

ios7 - AVAudioPlayer 不适用于 iOS7