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, он возвращает НЕТ. это все только в методе делегата. Я делаю что-то неправильно? ...NSCocoaErrorDomain - код 260 - person Tamil; 09.03.2014
comment
@Tamil добавьте редактирование в конец исходного вопроса с кодом для вашего метода didFinishDownloadingToURL. - person Rob; 09.03.2014
comment
@Rob Я использую downloadTaskWithURL с обработчиком завершения. Я хочу сделать перемещение файла из временного в постоянный файл вне основной очереди с помощью метода dispatch_async. Будет ли перемещен временный файл, если я сделаю это таким образом? Я получаю сообщение об ошибке, но оно выглядит по-другому. Полученная ошибка Error Domain = 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», который обрабатывает любую запись в файлы, удаление и т. Д. В приложении.

Проблема в том, что временный файл удаляется, как только завершается метод делегата, что происходило как раз в тот момент, когда я переключал потоки. Поэтому, когда я попытался получить доступ к файлу в моем потоке ввода-вывода файлов, он уже был удален.

Мое решение состояло в том, чтобы проанализировать файл в NSData в методе делегата, а затем использовать NSData для записи в файловую систему в моем потоке файлового ввода-вывода.

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