我正在使用AFNetworking下载我的应用用于同步解决方案的文件。在某些时候,应用程序会将一系列文件作为批处理单元下载。 Following this example,我这样运行批处理:
NSURL *baseURL = <NSURL with the base of my server>;
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
// as per: https://stackoverflow.com/a/19883392/353137
dispatch_group_t group = dispatch_group_create();
for (NSDictionary *changeSet in changeSets) {
dispatch_group_enter(group);
AFHTTPRequestOperation *operation =
[manager
POST:@"download"
parameters: <my download parameters>
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// handle download success...
// ...
dispatch_group_leave(group);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// handle failure...
// ...
dispatch_group_leave(group);
}];
[operation start];
}
// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// run code when all files are downloaded
});
这适用于批量下载。但是,我想向用户显示一个MBProgressHUD,向他们展示下载的进展情况。
AFNetworking提供回调方法
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
}];
...只需将进度设置为totalBytesRead / totalBytesExpectedToRead
,即可轻松更新进度表。但是,当您同时进行多次下载时,很难总体跟踪。
我考虑过为每个HTTP操作设置一个NSMutableDictionary
密钥,使用这种通用格式:
NSMutableArray *downloadProgress = [NSMutableArray arrayWithArray:@{
@"DownloadID1" : @{ @"totalBytesRead" : @0, @"totalBytesExpected" : @100000},
@"DownloadID2" : @{ @"totalBytesRead" : @0, @"totalBytesExpected" : @200000}
}];
随着每个操作的下载进展,我可以更新中心totalBytesRead
中针对该特定操作的NSMutableDictionary
- 然后将所有totalBytesRead
和totalBytesExpected' to come up with the total for the whole batched operation. However, AFNetworking's progress callback method
downloadProgressBlock合计, defined as
^(NSUInteger bytesRead,long long totalBytesRead,long long totalBytesExpectedToRead){} does not include the specific operation as a callback block variable (as opposed to the
成功and
failure`回调,它确实包含特定操作作为变量,使其可访问)。据我所知,这使得无法确定哪个操作专门进行回调。
有关如何使用AFNetworking跟踪多极同步下载进度的任何建议?
答案 0 :(得分:5)
如果你的块是内联的,你可以直接访问operation
,但编译器可能会警告你循环引用。您可以通过声明弱引用并在块内使用它来解决:
__weak AFHTTPRequestOperation weakOp = operation;
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSURL* url = weakOp.request.URL; // sender operation's URL
}];
实际上,您可以访问块内的任何内容,但是您需要了解块才能实现。通常,块中引用的任何变量在块创建时复制,即行执行的时间。这意味着当weakOp
行执行时,块中的weakOp
将引用setDownloadProgressBlock
变量的值。您可以认为它就像您在块中引用的每个变量一样,如果您的块立即执行了。
答案 1 :(得分:2)
只是为了使这些事情变得简单;-)
HERE您可以找到一个示例项目。只需按下+按钮并插入要下载的文件的直接URL即可。没有错误检查,也没有URL重定向,因此只插入直接URL
相关部分请查看DownloadViewController
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
这里的解释是:
当您将变量传递给块时,该块会复制从外部传递的变量。 因为我们的变量只是一个对象指针(一个内存地址),所以指针被复制到块内,并且由于默认存储是__strong,所以引用将被保持,直到块被销毁。
这意味着您可以传递给块直接引用您的进度视图(我使用UIProgressView,因为我从未使用过MBProgressHUD):
UIProgressView *progressView = // create a reference to the view for this specific operation
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
progressView.progress = // do calculation to update the view. If the callback is on a background thread, remember to add a dispatch on the main queue
}];
这样做,每个操作都会引用自己的progressView。保存引用并更新进度视图,直到进度块存在。
答案 2 :(得分:2)
您必须下载一些zip文件,视频文件等。 制作要下载的文件的模型,其中包含类似的字段 (id,url,image,type等...)
创建一个NSOperationQueue 并根据您的要求设置maxConcurrentOperationCount 公开方法。 (在单身课程中)
- (void)downloadfileModel:(ModelClass *)model {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip",model.iD]];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:model.url]];
operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setUserInfo:[NSDictionary dictionaryWithObject:model forKey:@"model"]];
////// Saving model into operation dictionary
[operation setProgressiveDownloadProgressBlock:^(AFDownloadRequestOperation *operation, NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
////// Sending Notification
////// Try to send notification only on significant download
totalBytesRead = ((totalBytesRead *100)/totalBytesExpectedToReadForFile);
[[NSNotificationCenter defaultCenter] postNotificationName:DOWNLOAD_PROGRESS object:model userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%lld",totalBytesRead] forKey:@"progress"]];
/// Sending progress notification with model object of operation
}
}];
[[self downloadQueue] addOperation:operation]; // adding operation to queue
}
使用不同的模型多次调用此方法以进行多次下载。
在控制器上观察该通知 在哪里显示下载进度。 (可能是tableView控制器)。 显示此课程中的所有下载操作列表
显示进度观察通知并从通知中获取模型对象并从通知中获取文件ID并在表视图中查找该ID并使用该进度更新该特定单元格。
答案 3 :(得分:0)
启动操作时,您可以在NSDictionary中将每个操作,downloadID和totalbytesRead和totalBytesExpected的值一起保护,并将所有dicts保存到downloadProgressArray。
然后,当调用回调方法时,循环遍历数组并将调用操作与每个字典中的操作进行比较。通过这种方式,您应该能够识别操作。
答案 4 :(得分:0)
我做了一些非常相似的事情(上传了一堆文件而不是下载)
这是解决问题的简单方法。 假设您一次下载最多10个文件。
__block int random=-1;
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
if (random == -1) // This chunk of code just makes sure your random number between 0 to 10 is not repetitive
{
random = arc4random() % 10;
if(![[[self myArray]objectAtIndex:random]isEqualToString:@"0"])
{
while (![[[self myArray]objectAtIndex:random]isEqualToString:@"0"])
{
random = arc4random() % 10;
}
}
[DataManager sharedDataManager].total += totalBytesExpectedToWrite;
}
[[self myArray] replaceObjectAtIndex:random withObject:[NSString stringWithFormat:@"%lu",(unsigned long)totalBytesWritten]];
然后你这样计算:
NSNumber * sum = [[self myArray] valueForKeyPath:@"@sum.self"];
float percentDone;
percentDone = ((float)((int)[sum floatValue]) / (float)((int)[DataManager sharedDataManager].total));
[self array]将如下所示:
array: (
0,
444840, // <-- will keep increasing until download is finished
0,
0,
0,
442144, // <-- will keep increasing until download is finished
0,
0,
0,
451580 // <-- will keep increasing until download is finished
)