NSURLSessionDownloadTask - изтегля се, но завършва с грешка

Опитвам се да изтегля pdf файл. по-рано, когато използвах блока за обработка на завършване, успях да видя файла в местоположението tmp. След това исках да покажа напредъка на изтеглянето, така че внедрих методите на делегиране. Но вече виждам лентата за напредъка да работи и файлът се изтегля. но след като изтеглянето приключи (записани байтове/общо байтове = 1), делегатът за грешка се извиква и няма файл в tmp местоположението. какво ми липсва по-долу е моят код. Качих проекта на https://www.dropbox.com/s/vn5zwfwx9izq60a/trydownload.zip

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:@"http://aayudham.com/URLLoadingSystem.pdf"]];
    [downloadTask resume];

}

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%@",[error localizedDescription]);
}
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    dispatch_async(dispatch_get_main_queue(), ^{
        _progressBar.progress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite;
        double value =(double)totalBytesWritten/(double)totalBytesExpectedToWrite;
        NSLog(@"%f",value);
    });
}

-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{

}

-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
       NSError *error;
//getting docs dir path
NSArray * tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [tempArray objectAtIndex:0];

//adding folder path
NSString *appDir = [docsDir stringByAppendingPathComponent:@"/Reader/"];

NSFileManager *fileManager = [NSFileManager defaultManager];

if(![fileManager fileExistsAtPath:appDir])
{
    [fileManager createDirectoryAtPath:appDir withIntermediateDirectories:NO attributes:nil error:&error];
}


BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:[appDir stringByAppendingString:@"/demo.pdf"] error:&error];

NSLog(fileCopied ? @"Yes" : @"No");
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

person Tamil    schedule 09.03.2014    source източник
comment
каква е грешката при влизане didCompleteWithError?   -  person sergio    schedule 09.03.2014
comment
Когато отпечатам [error localizedDescription] получавам Операцията не можа да бъде завършена. (Грешка какао 516.).   -  person Tamil    schedule 09.03.2014
comment
Успях да поправя проблема, като добавих име на файл в края на toPath и сега работи. Има ли друг ефикасен начин да направите това?   -  person Tamil    schedule 09.03.2014
comment
Не съм сигурен, че разбирам, но може би просто генериране на UUID като име на файл ще свърши работа...   -  person sergio    schedule 09.03.2014


Отговори (3)


@Rob, благодаря ви за бързите отговори и това ми помогна много. Ето моят код, който работи. Дано помогне на някого. Мога да получа действителното име на файла и да го преместя в директорията с документи, използвайки оригиналното име.

-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;

    //getting application's document directory path
    NSArray * tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsDir = [tempArray objectAtIndex:0];

    //adding a new folder to the documents directory path
    NSString *appDir = [docsDir stringByAppendingPathComponent:@"/Reader/"];

    //Checking for directory existence and creating if not already exists
    if(![fileManager fileExistsAtPath:appDir])
    {
        [fileManager createDirectoryAtPath:appDir withIntermediateDirectories:NO attributes:nil error:&error];
    }

    //retrieving the filename from the response and appending it again to the path
    //this path "appDir" will be used as the target path 
    appDir =  [appDir stringByAppendingFormat:@"/%@",[[downloadTask response] suggestedFilename]];

    //checking for file existence and deleting if already present.
    if([fileManager fileExistsAtPath:appDir])
    {
        NSLog([fileManager removeItemAtPath:appDir error:&error]?@"deleted":@"not deleted");
    }

    //moving the file from temp location to app's own directory
    BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:appDir error:&error];
    NSLog(fileCopied ? @"Yes" : @"No");

}
person Tamil    schedule 09.03.2014

В didFinishDownloadingToURL трябва да преместите файла от location на някое по-постоянно място (напр. папката ви с документи). Ако по-късно търсите този файл във временното местоположение, не се учудвам, че вече не е там.

Като документация казва, че location е дефиниран като такъв:

URL адрес на файл за временния файл. Тъй като файлът е временен, трябва или да го отворите за четене, или да го преместите на постоянно място в директорията на контейнера на пясъчника на вашето приложение, преди да се върнете от този метод на делегиране.

Трябва да преместите файла на новото му местоположение преди да се върнете от didFinishDownloadingToURL.

person Rob    schedule 09.03.2014
comment
Отстранявам грешки в приложението и веднага щом изтеглянето завърши, файлът липсва в папката tmp (не го местя никъде). докато изтеглянето не продължи, мога да видя временен файл, който се появява в папката tmp. - person Tamil; 09.03.2014
comment
@Tamil Актуализирах отговора си с препратката в документацията, която ви насърчава да преместите файла на постоянно място, преди да излезете от didFinishDownloadingToURL. ОС може да го премахне веднага щом се върнете от вашия делегиран метод. - person Rob; 09.03.2014
comment
Здравей @Rob, благодаря ти много, това свърши работа. но сега имам проблем с копирането на файла. Създадох директория в директорията с документи и се опитвах да преместя файла там с помощта на [fileManager copyItemAtPath:[location absoluteString] toPath:newPath error:&error], но това връща НЕ и файлът не се копира :( - person Tamil; 09.03.2014
comment
@Tamil Лично аз бих използвал [fileManager copyItemAtURL:location toURL:[NSURL fileURLWithPath:newPath] error:&error]. Но погледнахте ли обекта error? Това трябва да ви каже какво не е наред. - person Rob; 09.03.2014
comment
когато проверя fileExistsAtPath, той връща NO. всичко това е само в рамките на метода на делегата. Правя ли нещо нередно? ...NSCocoaErrorDomain - код 260 - person Tamil; 09.03.2014
comment
@Tamil добавете редакция в края на оригиналния си въпрос с кода за вашия didFinishDownloadingToURL метод. - person Rob; 09.03.2014
comment
@Rob Използвам downloadTaskWithURL с манипулатор за завършване. Искам да направя преместване на файл от временен към постоянен файл извън главната опашка с помощта на dispatch_async. Ще бъде ли преместен временният файл, ако го направя по този начин? Получавам грешка, но изглежда, че е различна, Получена грешка Домейн = NSCocoaErrorDomain Code=516 „CFNetworkDownload_aukj6k.tmp“ не може да бъде преместен в „1827B916-8B74-41CF-8868-537FAA8480CF“, защото елемент със същото име вече съществува.. Мога да започна нова публикация с пълен код, ако е необходимо. - person Smart Home; 17.08.2016
comment
@SmartHome - 1. Да, в бъдеще, моля, публикувайте свой собствен въпрос. 2. Вашата грешка не ви казва, че старият файл е премахнат, а по-скоро, че файл с избраното име вече съществува в желаната дестинация. 3. Наистина трябва да направите преместването на файла в опашката, която е извикана (или ако имате нужда от друга опашка поради причини за синхронизация, изпратете синхронно). 4. Преместването на файлове (за разлика от копирането на файлове) става много бързо, така че не се притеснявайте за проблеми с производителността. 5. Ако имате нужда от друга опашка, посочете това, когато настройвате сесия. - person Rob; 17.08.2016
comment
Благодаря много @Rob за вашия опит. Проблемът с преместването на файла се оказа, защото използвах препоръчания от Apple код, който се преместваше в директория. И така, грешката беше, че тази директория вече съществува. Смених го на име на файл и вече работи. Засега ще премахна dispatch_async, като използвам dispatch_async, ако при downloadTaskWithURL, както предложихте. - person Smart Home; 17.08.2016

Само в случай, че някой имаше същия проблем като мен, реших да публикувам решението си тук.

Проблемът ми беше, че предикатните методи се задействат във фонова нишка, така че изпращах към моята нишка „file io“, която обработва всяко писане във файлове, изтриване и т.н. в рамките на приложението.

Проблемът с това е, че временният файл се изтрива веднага щом делегираният метод приключи, което се случи в точния момент, когато превключих нишки. Така че, когато се опитах да осъществя достъп до файла в моята файлова io нишка, той вече беше изтрит.

Моето решение беше да анализирам файла в NSData в метода на делегиране, след което да използвам NSData, за да пиша във файловата система в моята файлова io нишка.

person Nick Kirsten    schedule 23.06.2015
comment
Този отговор наистина би могъл да използва някои пояснения и примери. - person jungledev; 07.10.2016