Как получить ход загрузки в AFNetworking 2.0?

Я использую AFURLSessionManager для создания новой задачи загрузки:

AFURLSessionManager* manager = ...

NSProgress* p = nil;
NSURLSessionDownloadTask* downloadTask =
        [manager downloadTaskWithRequest:request
                                 progress:&p
                              destination:^NSURL*(NSURL* targetPath, NSURLResponse* response) {...}
                        completionHandler:^(NSURLResponse* response, NSURL* filePath, NSError* error) {...}
        ];
[downloadTask resume];

Однако файл загружается нормально, как мне получать уведомления о ходе выполнения?

p всегда устанавливается равным нулю. Я зарегистрировал для этого проблему.

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

Похоже, эта область по-прежнему содержит ошибки в AFNetworking 2.0.

Есть идеи?


person gregschlom    schedule 02.10.2013    source источник
comment
Согласованный. Я не могу заставить загрузчик даже работать. Он немедленно отменяется.   -  person Miles    schedule 03.10.2013
comment
Если вы хотите быть совместимым с iOS6/7, вам следует использовать AFHTTPRequestOperation. AFURLSessionManager работает только на iOS 7. Я опубликовал ответ, показывающий, как получить ход загрузки с помощью AFHTTPRequestOperation.   -  person TPoschel    schedule 17.10.2013


Ответы (4)


Вы должны соблюдать свойство fractionCompleted вашего объекта NSProgress, используя KVO:

NSURL *url = [NSURL URLWithString:@"http://www.hfrmovies.com/TheHobbitDesolationOfSmaug48fps.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
NSProgress *progress;
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    // …
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    [progress removeObserver:self forKeyPath:@"fractionCompleted" context:NULL];
    // …
}];

[downloadTask resume];
[progress addObserver:self
            forKeyPath:@"fractionCompleted"
               options:NSKeyValueObservingOptionNew
               context:NULL];

Затем добавьте метод наблюдателя:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"fractionCompleted"]) {
        NSProgress *progress = (NSProgress *)object;
        NSLog(@"Progress… %f", progress.fractionCompleted);
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

Конечно, вы должны проверить параметры keyPath и/или object, чтобы решить, является ли это объектом/свойством, которое вы хотите наблюдать.

Вы также можете использовать метод setDownloadTaskDidWriteDataBlock: из AFURLSessionManager (от которого наследуется AFHTTPSessionManager), чтобы установить блок для получения обновлений о ходе загрузки.

[session setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
    NSLog(@"Progress… %lld", totalBytesWritten);
}];

Этот метод AFNetworking отображает метод URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: из протокола NSURLSessionDownloadDelegate в более удобный блочный механизм.

Кстати, реализация Apple KVO сильно нарушена. Я рекомендую использовать лучшую реализацию, подобную предложенной Майком Эшем, с MAKVONotificationCenter. Если вам интересно узнать, почему KVO от Apple не работает, прочитайте Наблюдение за ключом и значением выполнено правильно Майк Эш.

person Sendoa    schedule 15.10.2013
comment
Завершение работы приложения из-за необработанного исключения «NSInvalidArgumentException», причина: «-[__NSCFBackgroundDownloadTask FractionCompleted]: нераспознанный селектор отправлен в экземпляр 0xd888b40» - person Shmidt; 17.10.2013
comment
Свойство fractionCompleted принадлежит объекту NSProgress. - person Sendoa; 17.10.2013
comment
Имейте в виду, что в KVO вы не должны вызывать super, если обрабатываете уведомление. - person alejandromp; 14.02.2014
comment
Спасибо @alejandromp. Я только что отредактировал его. Кстати, я рекомендую лучшую реализацию KVO, подобную предложенной Майком Эшем: github.com/mikeash/MAKVONotificationCenter. - person Sendoa; 15.02.2014
comment
В общем, вы можете использовать шаблон NSStringFromSelector(@selector(fractionCompleted)) вместо использования непрозрачных строк компилятора, таких как @fractionCompleted. - person Cameron Lowell Palmer; 16.03.2014
comment
пропущенный типизированный NSUR должен быть NSURL, также возникает ошибка, поскольку управление достигает конца непустого блока - person Everus; 31.05.2014
comment
Как добавить дополнительные заголовки в AFHTTPSessionManager. Домен ошибки=NSURLErrorDomain Code=-1102 У вас нет разрешения на доступ к запрошенному ресурсу. UserInfo=0xb96ba30 NSLocalizedDescription=У вас нет разрешения на доступ к запрошенному ресурсу. Вот как я это делаю [self.sessionManager.requestSerializer setValue:@application/x-www-form-urlencoded forHTTPHeaderField:@Content-Type]; [self.sessionManager.requestSerializer setValue:[NSString stringWithFormat:@Bearer %@,authToken] forHTTPHeaderField:@Authorization]; но когда я проверяю заголовок запроса, он отображается как (null) - person Praveen-K; 25.07.2014
comment
Примечание. Вам нужно будет использовать setTaskDidSendBodyDataBlock для задач с данными (вместо setDownloadTaskDidWriteDataBlock, упомянутого в ответе). - person Manav; 09.09.2014
comment
@CameronLowellPalmer В чем преимущество генерации строки во время выполнения? За исключением случаев, когда это невозможно сделать. - person Amin Negm-Awad; 14.10.2016

Столкнулся с похожей проблемой, нашел решение.

Перейдите по ссылке ниже: http://cocoadocs.org/docsets/AFNetworking/2.0.1/Categories/UIProgressView+AFNetworking.html

#import <AFNetworking/UIKit+AFNetworking.h>

и используйте дополнительный метод, доступный для вашего UIProgressView

setProgressWithDownloadProgressOfTask: анимированный:

Как я это сделал:

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request  progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response){
    NSURL *documentsDirectoryPath = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) firstObject]];
    return [documentsDirectoryPath URLByAppendingPathComponent:[targetPath lastPathComponent]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error){
    NSLog(@"File downloaded to: %@", filePath);

}];

[self.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
[downloadTask resume];
person CKD    schedule 26.10.2013
comment
Может быть полезно отметить, что переменная менеджера в этом фрагменте кода является экземпляром AFURLSessionManager. - person Aaron Vegh; 25.11.2014
comment
Потрясающий пример! Спасибо! - person Genevios; 28.07.2019

Простые решения для Swift:

let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let sessionManager = AFURLSessionManager(sessionConfiguration: sessionConfiguration)
let request = NSURLRequest(URL: url)
let sessionDownloadTask = sessionManager.downloadTaskWithRequest(request, progress: nil, destination: { (url, response) -> NSURL in

            return destinationPath.URLByAppendingPathComponent(fileName) //this is destinationPath for downloaded file

            }, completionHandler: { response, url, error in

                //do sth when it finishes
        })

Теперь у вас есть 2 варианта:

  1. Использование UIProgressView и setProgressWithDownloadProgressOfTask:

    progressView.setProgressWithDownloadProgressOfTask(sessionDownloadTask, animated: true)
    
  2. Использование AFURLSessionManager и setDownloadTaskDidWriteDataBlock:

    sessionManager.setDownloadTaskDidWriteDataBlock { session, sessionDownloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
    
        let progress = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)
            //do sth with current progress
    }
    

В конце не забудьте про:

sessionDownloadTask.resume()
person Bartłomiej Semańczyk    schedule 09.12.2015

Для загрузки файла со статусом прогресса используйте этот код

 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSURL *URL = [NSURL URLWithString:@"http://..."];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];


NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress)
{
    NSLog(@"Progress: %f", downloadProgress.fractionCompleted);
    if (progressBlock) {
                    progressBlock(downloadProgress);
                }
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
{
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
{
    if (response && successBlock) {
                    successBlock(response,filePath);
                }
    NSLog(@"File downloaded to: %@", filePath);
}];

[downloadTask resume];
person DURGESH    schedule 21.03.2017