iOS的7 NSURLSession下载在后台多个文件 [英] iOS 7 NSURLSession Download multiple files in Background
问题描述
我想下载使用NSUrlSession文件的列表。
我有一个计算成功下载 @属性(非原子)INT downloadsSuccessfulCounter变量;
。当正在下载的文件我禁用下载按钮
。当计数器等于到下载列表的大小,我再次启用按钮和计数器设置为0。我的方法做到这一点:
- (无效)URLSession:(NSURLSession *)会议downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *){位置... [NSOperationQueue mainQueue] addOperationWithBlock:^ { downloadsSuccessfulCounter ++; 如果(downloadsSuccessfulCounter == self.downloadList.count){
的NSLog(@的所有下载完成); [self.syncButton的setEnabled:YES]; downloadsSuccessfulCounter = 0;
}
}];
}
一切工作正常,但是当我再次打开的ViewController我得到的消息与标识com.myApp后台URLSession已经存在!
。计数器没有被设置为0和UI元素(UIButtons,UILabels)都没有响应
我想这个问题是因为NSURLSession仍然是开放的,但我真的不知道它是如何工作的。
我已经尝试了所有的教程,但其中99%是只下载1个文件,不超过1 ...
任何想法?
下面是我的code:
...
@属性(非原子,强)NSURLSession *会议;
... - (无效)viewDidLoad中{
[超级viewDidLoad中]; 的appDelegate =(AppDelegate中*)[[UIApplication的sharedApplication]代表]; self.downloadList = [[NSMutableArray里的alloc]初始化]; NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@com.myApp];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration委托:自我delegateQueue:无];
}
当我preSS的下载按钮
我调用这个方法(
我有包含下载
对象的 NSURLSessionDownloadTask
)
- (无效){startDownload 的for(int i = 0; I< self.downloadList计数];我++){
下载* D = [self.downloadList objectAtIndex:i]; 如果(!d.isDownloading){
如果(d.taskIdentifier == -1){
d.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:d.downloadSource]]; }其他{
d.downloadTask = [self.session downloadTaskWithResumeData:fdi.taskResumeData];
} d.taskIdentifier = d.downloadTask.taskIdentifier;
[d.downloadTask简历]
d.isDownloading = YES;
}
}
}
当应用程序在背景:
- (无效)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)会话{
的AppDelegate *的appDelegate = [UIApplication的sharedApplication] .delegate; [self.session getTasksWithCompletionHandler:^(* NSArray的dataTasks,NSArray中* uploadTasks,NSArray中* downloadTasks){ 如果([downloadTasks计数] == 0){
如果(appDelegate.backgroundTransferCompletionHandler!=无){ 无效(^ completionHandler)()= appDelegate.backgroundTransferCompletionHandler; appDelegate.backgroundTransferCompletionHandler =零; [NSOperationQueue mainQueue] addOperationWithBlock:^ {
completionHandler(); UILocalNotification * localNotification = [[UILocalNotification的alloc]初始化];
localNotification.alertBody = @所有文件下载;
[UIApplication的sharedApplication] presentLocalNotificationNow:localNotification]; }];
}
}
}];
}
所以,正如我在评论中提到的问题是,每个文件都需要一个独特的NSURLSession,每个NSURLSession需要NSURLSessionConfiguration一个唯一的标识符。
我觉得你很接近 - 也许比我更合适在某些方面...
你只需要创建一个结构,通过唯一的ID成独特的配置,来填充唯一会话(说快10倍)。
下面是我所做的:
/ *
*检索的文件列表下载
*也使用该列表来实例化项目的大小
*在我的情况,我打开一个字符返回文本文件,我要下载的文件的名称
* /
- (无效){getMediaList * NSString的名单= @HTTP://myserver/media_list.txt
NSURLSession *会话= [NSURLSession sharedSession] //< - 基本会话
[会议dataTaskWithURL:[NSURL URLWithString:列表]
completionHandler:^(NSData的*数据,NSURLResponse *响应,NSError *错误){ * NSString的stringFromData = [[NSString的页头] initWithData:数据编码:NSUTF8StringEncoding]; //填充阵列
REMOTE_MEDIA_FILE_PATHS = [stringFromData componentsSeparatedByString:@\\ n];
[个体经营instantiateURLSessions:[REMOTE_MEDIA_FILE_PATHS计数]];
//开始第一个文件
[个体经营的GetFile:[REMOTE_MEDIA_FILE_PATHS objectAtIndex:downloadCounter]:downloadCounter]; //这个变量在开始为0
}]
简历];
}
/ *
*这台配置的阵列和会话到适当大小,
*还给出了一个唯一的ID,以每一个
* /
- (无效)instantiateURLSessions:(INT){大小 NSMutableArray里*配置= [NSMutableArray的阵列]
NSMutableArray里*会话= [NSMutableArray的阵列] 的for(int i = 0; I<大小;我++){
* NSString的指数= [的NSString stringWithFormat:@%i的,我]
* NSString的唯一标识符= @MyAppBackgroundSessionIdentifier_
唯一标识符= [唯一标识符stringByAppendingString:指数] [配置ADDOBJECT:[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:唯一标识符]];
[会议ADDOBJECT:[NSURLSession sessionWithConfiguration:[配置objectAtIndex:我]委托:自我delegateQueue:[NSOperationQueue mainQueue]]];
} NSURL_BACKGROUND_CONFIGURATIONS = [NSArray的arrayWithArray:配置]
NSURL_BACKGROUND_SESSIONS = [NSArray的arrayWithArray:会议];
}
/ *
*此设置每个文件的下载任务,基于该阵列的索引的
*它还并置的路径的实际文件
* /
- (空)的GetFile:(* NSString的)文件:(INT){指数
* NSString的= fullPathToFile REMOTE_MEDIA_PATH; //路径要服务器与文件
fullPathToFile = [fullPathToFile stringByAppendingString:文件] NSURL * URL = [NSURL URLWithString:fullPathToFile];
NSURLSessionDownloadTask * downloadTask = [[NSURL_BACKGROUND_SESSIONS objectAtIndex:指数] downloadTaskWithURL:URL];
[downloadTask简历]}
/ *
*最后,在我的委托方法,下载(该文件是从临时数据移动后)完成后,我检查,如果我做了,如果不与索引的更新计数器再次调用GetFiles方法
* /
- (无效)URLSession:(NSURLSession *)会议downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)位置
{ //获取文件目录网址
NSArray的*路径= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
的NSString * documentsDirectory = [路径objectAtIndex:0];
* NSString的数据路径= [documentsDirectory stringByAppendingPathComponent:LOCAL_MEDIA_PATH];
NSURL * customDirectory = [NSURL fileURLWithPath:数据通路]; //获取文件名和创建目标网址
* NSString的sendingFileName = [downloadTask.originalRequest.URL lastPathComponent]
NSURL *目标网址= [customDirectory URLByAppendingPathComponent:sendingFileName]; //将文件
NSError *误差=零;
*的NSFileManager文件管理= [的NSFileManager defaultManager]
如果([文件管理器moveItemAtURL:位置的toURL:目标网址错误:&放大器;错误]){ //列表
[个体经营listCustomDirectory] 如果(downloadCounter百分比抑制率REMOTE_MEDIA_FILE_PATHS计数] -1){
//增加计数器
downloadCounter ++; //开始下一个文件
[个体经营的GetFile:[REMOTE_MEDIA_FILE_PATHS objectAtIndex:downloadCounter]:downloadCounter];
}
其他{
//完成您的操作/ NOTIFY USER / ETC
}
}
其他{
的NSLog(@该死的错误%@,错误);
//做一些智能这里
}
}
I want to download a List of files using NSUrlSession.
I have a variable for counting the successful downloads @property (nonatomic) int downloadsSuccessfulCounter;
. While the files are being downloaded I disable the Download Button
. When the counter is equal to the download list size, I enable the button again and set the counter to 0. I do this in the method:
-(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;
}
}];
}
Everything is working fine, but when I open again the ViewController I get the message A background URLSession with identifier com.myApp already exists!
. The counter is not set to 0 and the UI elements (UIButtons, UILabels) are not responding.
I guess the problem is because the NSURLSession is still open but I'm not really sure about how it works.
I have tried all the tutorials, but 99% of them are only for downloading 1 file, not more than 1... Any ideas?
Here is my code:
...
@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];
}
When I press the Download Button
I call this method (
I have a Downloadable
object which contains a 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;
}
}
}
When the app is in Background:
-(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];
}];
}
}
}];
}
So, as I mentioned in my comments, the issue is that each File requires a unique NSURLSession, and each NSURLSession requires a NSURLSessionConfiguration with a unique identifier.
I think that you were close - and probably more proper than me in certain aspects... You just need to create a structure to pass unique IDs into unique Configurations, to populate unique Sessions (say that 10x fast).
Here's what I did:
/* * Retrieves the List of Files to Download * Also uses the size of that list to instantiate items * In my case, I load a character returned text file with the names of the files that I want to download */
- (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];
}
/* * This sets Arrays of Configurations and Sessions to the proper size * It also gives a unique ID to each one */
- (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];
}
/* * This sets up the Download task for each file, based off of the index of the array * It also concatenates the path to the actual file */
- (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];
}
/* * Finally, in my delegate method, upon the completion of the download (after the file is moved from the temp data), I check if I am done and if not call the getFiles method again with the updated counter for the index */
-(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
}
}
这篇关于iOS的7 NSURLSession下载在后台多个文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!