使用 AFNetworking 计算下载多个文件的总进度

Posted

技术标签:

【中文标题】使用 AFNetworking 计算下载多个文件的总进度【英文标题】:calculating total progress of downloading multiple file with AFNetworking 【发布时间】:2014-09-07 13:57:16 【问题描述】:

我想下载多个文件,然后向用户显示总进度。

但问题就在这里,我不知道我应该如何计算总进度。

这是我的工作: 首先我得到了预计从所有文件中接收的总字节数:

for (NSURL candidateUrl in UrlsList)

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
                        //setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
                        [request setHTTPMethod:@"HEAD"];

                        getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
                        [getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
                         
                             //setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
                              totalImagesBytesExpectedToRead += [operation.response expectedContentLength];


                          failure:^(AFHTTPRequestOperation *operation, NSError *error) 
                             NSLog(@"Error: %@", error);
                         ];

                        [operationQueue addOperation:getTotalImagesBytesOperation];

估计总文件大小后:

        //downloading images
    for (NSURL *imageUrl in imagesURLList) 
        NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
        AFImageRequestOperation *downloadImageOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request
                                                                                  imageProcessingBlock:nil

                                                                                               success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)                                                                                       
NSLog(@"success")

failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) 
                                                                                                       NSLog(@"%@", [error localizedDescription]);
                                                                                                   ];

        [operationQueue addOperation:downloadImageOperation];

        [downloadImageOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
        

          **HOW TO CALCULATE TOTAL PROGRESS**

我不知道如何计算总大小! 我们拥有的价值观: 以上方法为您提供的当前文件的 totalBytesOfAllTheFiles、totalBytesRead 和 totalBytesExpectedToRead、indexOfCurrentFile 和 countOfFiles。

小心 setDownloadProgressBlock 调用数百次。

有人知道吗? (抱歉代码格式错误!)

【问题讨论】:

你不把totalBytesRead除以totalBytesExpectedToRead吗? @evan.stoddard totalBytesRead/totalBytesExpectedToRead 给了我当前文件的完成百分比。不是整个文件! 首先考虑:你真的需要如此精确才能使用总字节数来计算进度吗?我使用“下载的总文件数”/“要下载的总文件数”来计算应用程序的进度。这要容易得多。 想一想您的前五个文件的总大小为 1 mb,而后五个文件的大小为 100 mb。这个计算不好。 在这种情况下,唯一的方法可能是在下载开始之前从网络某处的数据文件中读取“总大小”信息。如果这不可能,您必须重新考虑您的应用程序设计。在下载文件之前,您无法知道所有文件的大小。 【参考方案1】:

我是这样实现的: 首先创建一个NSOperationQueue:

// Add the operation to a queue
// It will start once added
//calculating images byte size
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
foreach (NSURL *candidateURL in urlList )

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
                    //setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
                    [request setHTTPMethod:@"HEAD"];

                    AFHTTPRequestOperation *getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
                    [getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
                     
                         //setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
                         totalImagesBytesExpectedToRead += [operation.response expectedContentLength];


                      failure:^(AFHTTPRequestOperation *operation, NSError *error) 
                         NSLog(@"Error: %@", error);
                     ];

                    [operationQueue addOperation:getTotalImagesBytesOperation];


 //downloading images which should be downloaded
for (NSURL *imageUrl in imagesShouldBeDownlaoded) 
    NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];

    AFHTTPRequestOperation *downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    //we must provide file path with file name and its extension
    NSString *fileName = [self getImageName:[imageUrl absoluteString]];
    downloadFileOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:[[ImageManager applicationDocumentsDirectory] stringByAppendingPathComponent:fileName] append:NO];

    [downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
     
         NSLog(@"file saved");

     failure:^(AFHTTPRequestOperation *operation, NSError *error)
     
         completionBlock(NO, error);
         NSLog(@"%@", [error localizedDescription]);
     ];

    [operationQueue addOperation:downloadFileOperation];

    [downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
     
         totalBytesDownloaded += bytesRead;

         //total progress from 0.0 to 1.0
         progress = ((float)totalBytesDownloaded/(float)totalImagesBytesExpectedToRead);
         if(progress == 1)
         
             completionBlock(YES, nil);
         
         progressCallback(progress);
    ];

operationQueue 作为一个 FIFO 列表,首先计算图像字节大小,然后开始下载所有图像

【讨论】:

请解释一下completionBlock和progressCallback的使用。另外,您从哪里开始操作,例如 [downloadFileOperation Start]。请发表评论。我有点卡在这里。 不需要显式启动操作,下载操作会通过添加操作到操作队列中隐式启动。另外,completionBlock 和 progressCallback 是我的自定义块,它们会通知我下载进度或下载失败或成功。【参考方案2】:

AFNetworking3 使用dispatch_groupNSProgress 来实现:

#import <Foundation/Foundation.h>
#import <AFNetworking.h>

@interface Server : NSObject

@property (nonatomic, strong) AFHTTPSessionManager *manager;
@property (nonatomic, strong) NSProgress *progress;

- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls;

@end



#import "Server.h"
@implementation Server

- (instancetype)init

    self = [super init];
    if (self) 
        _manager = [AFHTTPSessionManager manager];
        _manager.requestSerializer = [AFJSONRequestSerializer serializer];
        _manager.responseSerializer = [AFJSONResponseSerializer serializer];

        _progress = [NSProgress progressWithTotalUnitCount:0];
        [_progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
    
    return self;


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context 
    NSLog(@"fractionCompleted:%lf",self.progress.fractionCompleted);


- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls 
    //progress
    dispatch_group_t group = dispatch_group_create();
    _progress.totalUnitCount = urls.count;
    [urls enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) 
         dispatch_group_enter(group);
        NSURLRequest *request = [NSURLRequest requestWithURL:obj];
        NSURLSessionDownloadTask *task = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) 
         destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) 
            return [[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil] URLByAppendingPathComponent:[response suggestedFilename]];
         completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) 
            NSLog(@"one task completion");
            dispatch_group_leave(group);
        ];

        NSProgress *child = [self.manager downloadProgressForTask:task];
        [self.progress addChild:child withPendingUnitCount:1];
        [task resume];
    ];

    dispatch_group_notify(group, dispatch_get_main_queue(), ^
        NSLog(@"%lf", self.progress.fractionCompleted);
    );



@end

【讨论】:

以上是关于使用 AFNetworking 计算下载多个文件的总进度的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking/NSURLSession耗时长创建100多个任务下载文件

AFNetworking 无法在 ios iPad 中下载多个 m4a 文件

下载许多文件时 AFNetworking / NSURLSession 超时

如何使用 AFNetworking 上传多个文件

使用 AFDownloadRequestOperation 下载队列中的多个文件

使用 AFNetworking 上传多个图像或文件,