AFN多文件进度下载
http://blog.cnbang.net/tech/2371/
http://blog.cnbang.net/tech/2416/
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
http://bugly.qq.com/bbs/forum.php?
http://www.guokr.com/post/114121/mod=viewthread&tid=417&fromuid=6
http://www.guokr.com/post/116169/
http://www.guokr.com/blog/148613/
http://www.cnblogs.com/hyddd/archive/2009/01/07/1371292.html
http://www.cnblogs.com/polobymulberry/p/5140806.html
https://github.com/AFNetworking/AFNetworking/tree/3.1.0
作者:要上班的斌哥
链接:http://www.jianshu.com/p/c36159094e24
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最近在写的一个项目里面涉及到资源包的下载操作,由于用户在使用过程中有可能会同时下载多个资源包,那么就需要对下载功能进行设计和封装。最开始使用的是使用AFN的下载方法来进行实现的,不过当时项目需求要求不是太高,所以没有处理一些特殊情况,比如任务数量的控制,优先级还有断点下载等等。就简单的在一个单例Manager里面添加了一个字典,将下载资源包的URL地址作为Key,对应的downloadTask作为Value,这样就可以在想要的地方获取到相应的下载进度了。
@property (nonatomic, strong, nonnull) NSMutableDictionary <NSString *, NSURLSessionDownloadTask *> *downloadTaskDict;
由于现在需要统一管理下载的资源,而且为了表示“用户至上”,比如节约流量需要使用断点下载,包括暂停和继续、应用重启时的断点下载;显示下载速度和比例等等。同时为了控制下载的速度也对最大下载数量进行了限制,默认3个等等。so.....
先说下原理
这里使用了AFN的下载方法来完成下载功能,只不过需要对其进行一些扩展。因此我将每个下载任务封装成了一个模型<FKNetworkingDownloadModel>,以下为部分属性定义:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingProgressModel : NSObject
/*
* data length of the bytes written.
*/
@property (nonatomic, assign) int64_t totalBytesWritten;
/*
* the total bytes of the resource data.
*/
@property (nonatomic, assign) int64_t totalBytesExpectedToWrite;
/*
* download speed.
*/
@property (nonatomic, assign) int64_t downloadSpeed;
/*
* download progress.
*/
@property (nonatomic, assign) float downloadProgress;
/*
* download left time
*/
@property (nonatomic, assign) int32_t downloadLeft;
@end
NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingDownloadModel : NSObject
/*
* resource URLString
*/
@property (nonatomic, copy) NSString *resourceURLString;
/*
* fileName default is resourceURLString last component
*/
@property (nonatomic, copy) NSString *fileName;
/**
* file directory
*/
@property (nonatomic, copy) NSString *fileDirectory;
/**
* file path
*/
@property (nonatomic, copy, nullable) NSString *filePath;
/**
* the plist file path, the plist file include information of the download task mission.
*/
@property (nonatomic, copy, nullable) NSString *plistFilePath;
/**
* record the download time when receive the data from the serverce, used to calculate download speed
*/
@property (nonatomic, strong) NSDate *downloadDate;
/**
* check the download state when a model alloc.
*/
//@property (nonatomic, assign) FKDownloadModelState modelState;
/**
* resume data, marked the position of the download mission.
*/
@property (nonatomic, strong, nullable) NSData *resumeData;
/*
* download task
*/
@property (nonatomic, strong, nullable) NSURLSessionDownloadTask *downloadTask;
/*
* progress
*/
@property (nonatomic, strong, nullable) FKNetworkingProgressModel *progressModel;
/**
* init method
*
* @param URLString resourceURLString
*
* @return
*/
-(instancetype)initWithResourceURLString:(NSString *)URLString;
@end
NS_ASSUME_NONNULL_END
这样可以将每一个下载的资源封装成一个Model,这样在使用管理类来控制下载的时候就会显得比较轻松。
在使用AFN之前需要补充说明一下NSURLSessionTask的一些知识:
抽象类(父类):NSURLSessionTask 包含了一些属性,其中使用到的属性包括:
//请求信息,包括URL地址等。
@property (nullable, readonly, copy) NSURLRequest *originalRequest;
@property (nullable, readonly, copy) NSURLRequest *currentRequest;
//响应信息,包括响应数据的长度和地址等等
@property (nullable, readonly, copy) NSURLResponse *response;
//下面两个属性是会使用到的,一个是已经接收到的数据长度和总共需要接受的数据长度,根据这个我们可以算出下载的进度信息
@property (readonly) int64_t countOfBytesReceived;
@property (readonly) int64_t countOfBytesExpectedToReceive;
//任务描述,我这里用model对应的URL地址来赋值
@property (nullable, copy) NSString *taskDescription;
//状态(取消、完成、进行中等)
@property (readonly) NSURLSessionTaskState state;
同时父类也提供了几个方法
- (void)cancel;//取消下载任务
- (void)suspend;//挂起(这个一般用于程序在运行中的时候暂停下载任务)
- (void)resume;//恢复下载任务
理论上说,如果不做应用重启后的断点下载,通过上面的步骤已经可以完成大部分的下载操作了,但是在程序重启时并不能够获取到对应的已下载的进度(downloadTask会将下载的资源放入tmp路径下面,以'CFNetworking'开头),那这个时候执行下载任务那么会重新下载。考虑到用户的流量,就必须在重启后的进行断点下载,因此我们需要使用到子类NSURLSessionDownloadTask
的一个方法
- (void)cancelByProducingResumeData:(void (^)(NSData * __nullable resumeData))completionHandler;
这个方法可以讲当前的task已下载的数据使用一个data保存起来, 然后使用NSURLSession
的方法可以根据这个data从服务器获取后续的数据
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
断点下载的原理大概就是这样,接下来是考虑管理类的设计和封装。
管理类的设计和封装
思路:使用单例来管理下载任务,同时可以指定最大的下载数和单任务和多任务模式的切换。关于下载的控制(开始、暂停、恢复和断点下载)都可以通过AFN相应的方法来实现
ANF里面关于下载有几个方法,在<AFURLSessionManager>类里面可以找到
//这个方法是经常用到的一个,生成一个NSURLRequest,然后根据这个请求开始下载任务可以追踪到下载的进度,需要设置下载完成后的文件移动到的位置,最后是完成后的回调信息
-(NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//该方法根据一个resumeData来进行断点下载,我这里断点下载的实现就是根据这个来的,resumeData已经在模型里面了。
在下载cancel或者suspend的时候只需要将model的resumeData赋值,然后写入本地plist文件,下次下载的时候根据plist信息获取下载的一些信息就OK了
-(NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
下面附上一头文件中的一些代码
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
#import "FKNetworkingDownloadModel.h"
NS_ASSUME_NONNULL_BEGIN
NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingManager : NSObject
/**
* download root file path. default is '~/cache/forkid.networking.manager1.0'
*/
@property (nonatomic, copy, readonly) NSString *downloadDirectory;
/**
* contains the models whose is downloading.
*/
@property (nonatomic, strong, readonly) NSMutableArray <__kindof FKNetworkingDownloadModel *> *downloadingModels;
/**
* contains the models whose is waiting for download.
*/
@property (nonatomic, strong, readonly) NSMutableArray <__kindof FKNetworkingDownloadModel *> *waitingModels;
/**
* max download mission number.
*/
@property (nonatomic, assign) NSInteger maxDownloadCount;
/**
* first in and fisrt out.
*/
@property (nonatomic, assign) BOOL resumeTaskFIFO;
/**
* ignore maxDownloadCount. manager will resume all downloadTask.
*/
@property (nonatomic, assign, getter=isBatchDownload) BOOL batchDownload;
/**
* singleton
*/
+(FKNetworkingManager *)shareManager;
/**
===============> Description of download start <=============
= when you wanna download a file from serverce, you can use next methods as you need.
= there have some methods such as start, resume, cancel and so on.
= We add the 'cancel' method to pause a task instead of suspend, because we need to resume the task when the App restart.
= all methods will use a download model who is subclass of the 'FKNetworkingDownloadModel' for convenience.
= and every model use a URL string as the primary key to mark a download mission or task.
===============> Description of download end. <=============
*/
/**
* this method used to start a download mission with a download model, notice that the download model can't be nil, or the download mission will not execute.
*
* @param downloadModel download model
* @param progress progress of the download, track to refresh UI and ...
* @param completionHandler you can doing something after download mission completed, such as refresh your UI and move the file and so on.
*/
-(void)fk_startDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel
progress:(void (^)(FKNetworkingDownloadModel *downloadModel))progress
completionHandler:(void (^)(FKNetworkingDownloadModel *downloadModel, NSError * _Nullable error))completionHandler;
/**
* resume a download task with a download model, it will use the download model's 'resumeData'
*
* @param downloadModel download model
*/
-(void)fk_resumeDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
/**
* suspend or cancel a download task
*
* @param downloadModel download model
*/
-(void)fk_cancelDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
/**
* check the resourece has been downloaded or not from the download model resourceURL.
*
* @param downloadModel download model
*
* @return YES or NO.
*/
-(BOOL)fk_hasDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
/**
* delete local file if exist.
*
* @param downloadModel download model.
*/
-(void)fk_deleteDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
/**
* delete all downloaded files.
*/
-(void)fk_deleteAllDownloadedFiles;
/**
* get a download model, which is downloading with a URLString. if there is not exist a model, will return nil.
*
* @param URLString URLString.
*
* @return download model
*/
-(nullable FKNetworkingDownloadModel *)fk_getDownloadingModelWithURLString:(NSString *)URLString;
/**
* get download progress information. such as download speed, progress and so on.
*
* @param downloadModel download model.
*
* @return progress model.
*/
-(nullable FKNetworkingProgressModel *)fk_getDownloadProgressModelWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
@end
NS_ASSUME_NONNULL_END
定义完上述的方法后,在实现文件中实现相应的方法即可,因为采用AFN,所以我们不用写太多的步骤,只需要关心流程控制,和线程控制就行了。
下面是部分的实现代码:
#import "FKNetworkingManager.h"
#import "NSObject+FKAdd.h"
NSString *const FKNetworkingManagerFileName = @"forkid.networking.manager1.0";
@interface FKNetworkingManager ()
/**
* AFNetworking manager.
*/
@property (nonatomic, strong) AFHTTPSessionManager *AFManager;
/**
* download root directory.
*/
@property (nonatomic, copy) NSString *downloadDirectory;
/**
* fileManager to manage download files
*/
@property (nonatomic, strong) NSFileManager *fileManager;
/*
* the models for waiting for download, the elements should be FKDownloadModel and it's subClasses
*/
@property (nonatomic, strong) NSMutableArray <__kindof FKNetworkingDownloadModel *> *waitingModels;
/*
* the models whose being downloaded, the elements should be FKDownloadModel and it's subClasses
*/
@property (nonatomic, strong) NSMutableArray <__kindof FKNetworkingDownloadModel *> *downloadingModels;
/*
* key-values dictionary of the downloadModels, format as '<NSString *key, FKDownloadModel *model>' to make constraints
* used to find a downloadModel from this container,
* when the program will terminate, container will be clear
*/
@property (nonatomic, strong) NSMutableDictionary <NSString *, __kindof FKNetworkingDownloadModel *> *downloadModelsDict;
@end
NSInteger const fk_timeInterval = 5;
@implementation FKNetworkingManager
#pragma mark - download methods
-(void)fk_startDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel
progress:(void (^)(FKNetworkingDownloadModel * _Nonnull))progress
completionHandler:(void (^)(FKNetworkingDownloadModel * _Nonnull, NSError * _Nullable))completionHandler{
NSString *fileName = [downloadModel.fileName componentsSeparatedByString:@"."].firstObject;
downloadModel.fileDirectory = [self.downloadDirectory stringByAppendingPathComponent:fileName];
downloadModel.filePath = [[self.downloadDirectory stringByAppendingPathComponent:fileName] stringByAppendingPathComponent:downloadModel.fileName];
downloadModel.plistFilePath = [downloadModel.fileDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", fileName]];
if (![self canBeStartDownloadTaskWithDownloadModel:downloadModel]) return;
downloadModel.resumeData = [NSData dataWithContentsOfFile:downloadModel.plistFilePath];
if (downloadModel.resumeData.length == 0) {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadModel.resourceURLString]];
downloadModel.downloadTask = [self.AFManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
[self setValuesForDownloadModel:downloadModel withProgress:downloadProgress.fractionCompleted];
progress(downloadModel);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [NSURL fileURLWithPath:downloadModel.filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if (error) {
[self fk_cancelDownloadTaskWithDownloadModel:downloadModel];
completionHandler(downloadModel, error);
}else{
[self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
completionHandler(downloadModel, nil);
[self deletePlistFileWithDownloadModel:downloadModel];
}
}];
}else{
downloadModel.progressModel.totalBytesWritten = [self getResumeByteWithDownloadModel:downloadModel];
downloadModel.downloadTask = [self.AFManager downloadTaskWithResumeData:downloadModel.resumeData progress:^(NSProgress * _Nonnull downloadProgress) {
[self setValuesForDownloadModel:downloadModel withProgress:[self.AFManager downloadProgressForTask:downloadModel.downloadTask].fractionCompleted];
progress(downloadModel);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [NSURL fileURLWithPath:downloadModel.filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if (error) {
[self fk_cancelDownloadTaskWithDownloadModel:downloadModel];
completionHandler(downloadModel, error);
}else{
[self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
completionHandler(downloadModel, nil);
[self deletePlistFileWithDownloadModel:downloadModel];
}
}];
}
if (![self.fileManager fileExistsAtPath:self.downloadDirectory]) {
[self.fileManager createDirectoryAtPath:self.downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
[self createFolderAtPath:[self.downloadDirectory stringByAppendingPathComponent:fileName]];
[self fk_resumeDownloadWithDownloadModel:downloadModel];
}
-(void)fk_resumeDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if (downloadModel.downloadTask) {
downloadModel.downloadDate = [NSDate date];
[downloadModel.downloadTask resume];
self.downloadModelsDict[downloadModel.resourceURLString] = downloadModel;
[self.downloadingModels addObject:downloadModel];
}
}
-(void)fk_cancelDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if (!downloadModel) return;
NSURLSessionTaskState state = downloadModel.downloadTask.state;
if (state == NSURLSessionTaskStateRunning) {
[downloadModel.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
downloadModel.resumeData = resumeData;
@synchronized (self) {
BOOL isSuc = [downloadModel.resumeData writeToFile:downloadModel.plistFilePath atomically:YES];
[self saveTotalBytesExpectedToWriteWithDownloadModel:downloadModel];
if (isSuc) {
downloadModel.resumeData = nil;
[self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
[self.downloadingModels removeObject:downloadModel];
}
}
}];
}
}
-(void)fk_deleteDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if ([self.fileManager fileExistsAtPath:downloadModel.fileDirectory]) {
[self.fileManager removeItemAtPath:downloadModel.fileDirectory error:nil];
}
}
-(void)fk_deleteAllDownloadedFiles{
if ([self.fileManager fileExistsAtPath:self.downloadDirectory]) {
[self.fileManager removeItemAtPath:self.downloadDirectory error:nil];
}
}
-(BOOL)fk_hasDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if ([self.fileManager fileExistsAtPath:downloadModel.filePath]) {
NSLog(@"已下载的文件...");
return YES;
}
return NO;
}
-(FKNetworkingDownloadModel *)fk_getDownloadingModelWithURLString:(NSString *)URLString{
return self.downloadModelsDict[URLString];
}
-(FKNetworkingProgressModel *)fk_getDownloadProgressModelWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
FKNetworkingProgressModel *progressModel = downloadModel.progressModel;
progressModel.downloadProgress = [self.AFManager downloadProgressForTask:downloadModel.downloadTask].fractionCompleted;
return progressModel;
}
#pragma mark - private methods
-(BOOL)canBeStartDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if (!downloadModel) return NO;
if (downloadModel.downloadTask && downloadModel.downloadTask.state == NSURLSessionTaskStateRunning) return NO;
if ([self fk_hasDownloadedFileWithDownloadModel:downloadModel]) return NO;
return YES;
}
-(void)setValuesForDownloadModel:(FKNetworkingDownloadModel *)downloadModel withProgress:(double)progress{
NSTimeInterval interval = -1 * [downloadModel.downloadDate timeIntervalSinceNow];
downloadModel.progressModel.totalBytesWritten = downloadModel.downloadTask.countOfBytesReceived;
downloadModel.progressModel.totalBytesExpectedToWrite = downloadModel.downloadTask.countOfBytesExpectedToReceive;
downloadModel.progressModel.downloadProgress = progress;
downloadModel.progressModel.downloadSpeed = (int64_t)((downloadModel.progressModel.totalBytesWritten - [self getResumeByteWithDownloadModel:downloadModel]) / interval);
if (downloadModel.progressModel.downloadSpeed != 0) {
int64_t remainingContentLength = downloadModel.progressModel.totalBytesExpectedToWrite - downloadModel.progressModel.totalBytesWritten;
int currentLeftTime = (int)(remainingContentLength / downloadModel.progressModel.downloadSpeed);
downloadModel.progressModel.downloadLeft = currentLeftTime;
}
}
-(int64_t)getResumeByteWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
int64_t resumeBytes = 0;
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:downloadModel.plistFilePath];
if (dict) {
resumeBytes = [dict[@"NSURLSessionResumeBytesReceived"] longLongValue];
}
return resumeBytes;
}
-(NSString *)getTmpFileNameWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
NSString *fileName = nil;
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:downloadModel.plistFilePath];
if (dict) {
fileName = dict[@"NSURLSessionResumeInfoTempFileName"];
}
return fileName;
}
-(void)createFolderAtPath:(NSString *)path{
if ([self.fileManager fileExistsAtPath:path]) return;
[self.fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
-(void)deletePlistFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
if (downloadModel.downloadTask.countOfBytesReceived == downloadModel.downloadTask.countOfBytesExpectedToReceive) {
[self.fileManager removeItemAtPath:downloadModel.plistFilePath error:nil];
[self removeTotalBytesExpectedToWriteWhenDownloadFinishedWithDownloadModel:downloadModel];
}
}
-(NSString *)managerPlistFilePath{
return [self.downloadDirectory stringByAppendingPathComponent:@"ForKidManager.plist"];
}
-(nullable NSMutableDictionary <NSString *, NSString *> *)managerPlistDict{
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:[self managerPlistFilePath]];
return dict;
}
-(void)saveTotalBytesExpectedToWriteWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
NSMutableDictionary <NSString *, NSString *> *dict = [self managerPlistDict];
[dict setValue:[NSString stringWithFormat:@"%lld", downloadModel.downloadTask.countOfBytesExpectedToReceive] forKey:downloadModel.resourceURLString];
[dict writeToFile:[self managerPlistFilePath] atomically:YES];
}
-(void)removeTotalBytesExpectedToWriteWhenDownloadFinishedWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
NSMutableDictionary <NSString *, NSString *> *dict = [self managerPlistDict];
[dict removeObjectForKey:downloadModel.resourceURLString];
[dict writeToFile:[self managerPlistFilePath] atomically:YES];
}
#pragma mark - share instance
+(FKNetworkingManager *)shareManager{
static FKNetworkingManager *manager = nil;
static dispatch_once_t sigletonOnceToken;
dispatch_once(&sigletonOnceToken, ^{
manager = [[self alloc] init];
});
return manager;
}
- (instancetype)init{
self = [super init];
if (self) {
_AFManager = [[AFHTTPSessionManager alloc]init];
_AFManager.requestSerializer.timeoutInterval = 5;
_AFManager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;//NSURLRequestUseProtocolCachePolicy;
NSSet *typeSet = [NSSet setWithObjects:@"application/json", @"text/plain", @"text/javascript", @"text/json", @"text/html", nil];
_AFManager.responseSerializer.acceptableContentTypes = typeSet;
_AFManager.securityPolicy.allowInvalidCertificates = YES;
_maxDownloadCount = 1;
_resumeTaskFIFO = YES;
_batchDownload = NO;
_fileManager = [NSFileManager defaultManager];
_waitingModels = [[NSMutableArray alloc] initWithCapacity:1];
_downloadingModels = [[NSMutableArray alloc] initWithCapacity:1];
_downloadModelsDict = [[NSMutableDictionary alloc] initWithCapacity:1];
_downloadDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:FKNetworkingManagerFileName];
[_fileManager createDirectoryAtPath:_downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
NSDictionary <NSString *, NSString *> *plistDict = [[NSDictionary alloc] init];
NSString *managerPlistFilePath = [_downloadDirectory stringByAppendingPathComponent:@"ForKidManager.plist"];
[plistDict writeToFile:managerPlistFilePath atomically:YES];
}
return self;
}
@end
转自:http://www.jianshu.com/p/f311f7057faa
AFN多文件进度下载的更多相关文章
- iOS- 利用AFNetworking3.0+(最新AFN) - 实现文件断点下载
官方建议AFN的使用方法 0.导入框架准备工作 •1. 将AFNetworking3.0+框架程序拖拽进项目 •2. 或使用Cocopod 导入AFNetworking3.0+ •3. ...
- iOS- 利用AFNetworking(AFN) - 实现文件断点下载
https://www.cnblogs.com/qingche/p/3500746.html 1. 定义一个全局的AFHttpClient:包含有 1> baseURL 2> 请求 3&g ...
- IOS下载查看PDF文件(有下载进度)
IOS(object-c) 下载查看 PDF 其实还是蛮容易操作的.在下载前,首先要把 IOS 可以保存文件的目录给过一遍: IOS 文件保存目录 IOS 可以自定义写入的文件目录,是很有限的,只能是 ...
- 使用 new XMLHttpRequest() 制作下载文件进度条
mui 进度控件使用方法: 检查当前容器(container控件)自身是否包含.mui-progressbar类: 当前容器包含.mui-progressbar类,则以当前容器为目标控件,直接显示进度 ...
- 原生网络请求以及AFN网络请求/异步下载
这里对网络请求方式做一个总结. 原生方式同步GET请求: NSString *urlStr = @"http://apis.juhe.cn/mobile/get?phone=13429667 ...
- iOS- 利用AFNetworking3.0+(最新AFN) - 实现文件上传
官方建议AFN的使用方法 0.导入框架准备工作 •1. 将AFNetworking3.0+框架程序拖拽进项目 •2. 或使用Cocopod 导入AFNetworking3.0+ •3. 引入 ...
- [No00006B]方便的网络下载工具wget 可下载网站目录下的所有文件(可下载整个网站)
wget是linux下命令行的下载工具,功能很强大,它能完成某些下载软件所不能做的,比如如果你想下载一个网页目录下的所有文件,如何做呢?网络用户有时候会遇到需要下载一批文件的情况,有时甚至需要把整个网 ...
- iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载+使用输出流代替文件句柄
前言:本篇讲解,在前篇iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载的基础上,使用输出流代替文件句柄实现大文件断点续传. 在实际开发中,输入输出流用的比较少,但 ...
- DE1-SOC的sof文件无法下载解决办法
按照前面友晶科技的文档介绍一步步的做,后面发现根本无法下载sof文件. 通常思维: 一,器件选错了.器件选择错误会导致sof文件无法下载,至于这个ID具体怎么识别我没有追究.如果是这种错误Quartu ...
随机推荐
- Android源码和内核源码的下载,编译和执行
笔者依据罗升阳老师的<Android 系统源码情景分析>一书,尝试下载,编译和执行Android源码和内核源码.但可能是软件源"被墙"或版本号更新的原因.期间遇到诸多问 ...
- ElasticSearch获取指定Field数据的Java方法
ElasticSearch(ES)检索后需要结果时,可能通过source接口读出.但是这样的话,返回的结果会很多.在调用search方法时,我们可以添加addfield或addfields方法,仅仅读 ...
- selenium模拟键盘操作
单键 /** * 模拟键盘回车事件 * @throws AWTException */ public void KeyEventEnter() throws AWTException { Robot ...
- IOS 编程中引用第三方的方类库的方法及常见问题
方法一:直接复制全部源文件到项目中 这样的方法就是把第三方类库的全部源文件拷贝到项目中,直接把全部.h和.m文件拖到XCode项目中就可以. 注意: 1. 假设第三方类库引用了一些系统自带类库,那么在 ...
- OSQL.EXE 命令行下脱裤mssql
cd C:\Program Files\Microsoft SQL Server\100\Tools\Binn\ OSQL.EXE -S "localhost" -U " ...
- Android SDK安装时出错“android Failed to rename directory”的解决方法
Android SDK安装时出错"android Failed to rename directory"的解决的方法 安装Android SDK时遇到Failed to r ...
- java起源和基本数据类型
1.Java起源于1994年的sun公司,起初并没有体现出它独特的优势.sun公司一度想要放弃这个项目.亏的领头人的力争.Java才的以存在.二十世纪末.随着互联网的兴起.交互式设计越来越多的应用,对 ...
- js原生方式实现bind方法
1.思路 (1)因为bind方法不会立即执行函数,需要返回一个待执行的函数(这里用到闭包,可以返回一个函数)return function(){} (2)作用域绑定,这里可以使用apply或者call ...
- .net用url重写URLReWriter实现任意二级域名
.net用url重写URLReWriter实现任意二级域名 这两天需要用到URLReWriter来搞那个猪头的Blog,网上看到篇好文,收藏 摘要:解释了url重写的相关知识.用asp.net实现二级 ...
- php匿名函数和闭包函数及use关键字传参及Closure匿名函数类
php闭包函数用use传参有什么意义?答:use引用外层变量,比如全局变量 Closure,匿名函数,是php5.3的时候引入的,又称为Anonymous functions.字面意思也就是没有定义名 ...