美文网首页理论我的iOS网络
iOS开发笔记网络篇-使用AFN的download方法实现文件的

iOS开发笔记网络篇-使用AFN的download方法实现文件的

作者: ForKid | 来源:发表于2016-07-28 14:36 被阅读3607次
    吐槽 o(╯□╰)o

    最近在写的一个项目里面涉及到资源包的下载操作,由于用户在使用过程中有可能会同时下载多个资源包,那么就需要对下载功能进行设计和封装。最开始使用的是使用AFN的下载方法来进行实现的,不过当时项目需求要求不是太高,所以没有处理一些特殊情况,比如任务数量的控制,优先级还有断点下载等等。就简单的在一个单例Manager里面添加了一个字典,将下载资源包的URL地址作为Key,对应的downloadTask作为Value,这样就可以在想要的地方获取到相应的下载进度了。

    @property (nonatomic, strong, nonnull) NSMutableDictionary <NSString *, NSURLSessionDownloadTask *> *downloadTaskDict;
    

    由于现在需要统一管理下载的资源,而且为了表示“用户至上”,比如节约流量需要使用断点下载,包括暂停和继续、应用重启时的断点下载;显示下载速度和比例等等。同时为了控制下载的速度也对最大下载数量进行了限制,默认3个等等。so.....

    silence.jpg
    先说下原理

    这里使用了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
    

    如有问题或者BUG 欢迎指正

    前段时间由于工作上的事情耽搁了,所以很多朋友问的Demo迟迟没有上传上去,这段时间我会完善当前的功能整理下传上去,简要的修复一点小bug和完善一下上传和POST方法以及网络请求管理。

    未完待续...

    相关文章

      网友评论

      • 心语风尚:didReceiveResponse 是首次用新的URL下载相应调用 如果暂停 然后继续下同一个url这个方法会再次调用吗 是同一个url 暂停再继续
      • 牛巴粉带走:楼主,最近接触的项目刚好需要下载功能的管理,文章贴的代码挺好的,但是不够详细,请问什么时候出demo?
      • 楚简约:demo....还没出来么, 夭折~
      • sll_:有个问题讨论一下博主。afn下载文件 是不是保存到相同路径的文件 默认不覆盖的?
      • QQWeb1234qwe:博主 有demo没
      • 捡男孩的小破烂: 博主,demo写了么?
      • returnzyf:还是不要用downloadTask比较好,有点坑,非要执行取消才能实现缓存,用datatask,自己用filehandler拼接数据到沙盒比较好
        ForKid:@returnzyf 这个看需求吧,一般小一点的没啥区别,大的文件还是建议用data.
      • 奔哥小木屋:有demo么....
      • 奔哥小木屋:博主 model的.m里面是什么代码?
      • NateLam:楼主求问demo弄完了吗:flushed:
      • 心语风尚:demo呢
      • 兵兵哥哥:求demo啊
      • 东方奇迹:求demo!!!
      • a72cb3f812b5:楼主完善好了吗?给个联系方式啊
      • JohnQ:博主 什么时候整理demo发到git上啊
      • XTShow:博主您好,想请教您一下,如果下载中app强制退出(双击home从列表中退出),那么此时应该使用afn的cancelByProducingResumeData方法,保存ResumeData,然后下次在进入app的时候,使用ResumeData继续下载;我使用的是监听UIApplicationWillTerminateNotification的通知,接收到通知时执行的方法中使用cancelByProducingResumeData方法,但是此处这个方法不被调用?!监听执行方法中的打印都执行了,但是cancelByProducingResumeData就是不执行,但是如果发cancelByProducingResumeData方法放在正常的按钮点击事件中就是完全正常的!这是为什么呢?是我调用的时机不对吗?那么请问博主如何监听app被强制退出呢?谢谢~~~
        XTShow:刚才又增加了几个断点,发现cancelByProducingResumeData方法是执行的,但是block中的东西不执行。。。例如写文件这个[downloadModel.resumeData writeToFile:downloadModel.plistFilePath atomically:YES];就不执行(也不是完全不可能执行,在不改变任何东西的情况下,十几次里,可能有一次执行。。。)
      • Real_young:请问实现可以下载一个ipa包的链接吗?如何实现下载并安装啊。
        ForKid:下载没问题,安装这个呢不是这个能决定的,安装和系统有关
      • 一只霸天犬v:求demo 楼主 可以加q交流吗
      • 2cb466bd239b:上面的maxDownloadCount 好像没有任何作用啊,是怎么做的现在队列管理和下载数的限制呢
        ForKid:可以加Q
        2cb466bd239b:我现在正在为管理文件下载队列发愁,能不能交流一下,或者您有demo能学习一下吗
        ForKid:@自渔 这些东西没有更新:sweat_smile: 这个的作用其实是为了防止多个任务同时下载时下载速度过慢
      • 雪_晟:求 git 地址啊
      • 一抹果茶:感谢楼主的分享,期待github地址
        ForKid:@一抹果茶 恩恩 好的 最近在完善一些东西 包括 upload 和 post 的一些常用方法 搞定了以后第一个时间发出来 谢谢支持
      • 669f4f27f444:可否先发一份到我的邮箱,devilon@163.com 谢!
      • 669f4f27f444:github地址是多少???期待!!
        669f4f27f444:@ForKid 我项目有个下载的功能,就是想用你这种思路来管理,希望能及早用上,谢回复!!
        ForKid:@tonylegend 之前说准备弄Demo 中途换了工作 有点忙 然后现在是赶项目 尽量抽空吧 这个还需要完善
      • LannisZheng:多谢博主 受益了,请问demo上传了么 github地址是多少
        huzhiyong:@LannisZheng @ForKid 同问博主
      • 47号同学:progressModel没有呢 有没有demo呢?
        ForKid:@B2WuXX 这两天整理一下 到时候传到git上面
      • ForKid:核心的代码都在这里了 demo需要整理
      • 长期接iOS外包:有木有Demo?博主

      本文标题:iOS开发笔记网络篇-使用AFN的download方法实现文件的

      本文链接:https://www.haomeiwen.com/subject/szvvjttx.html