@class YGFileDownloader;
@protocol YGFileDownloaderDelegate <NSObject>
-(void)fileDownloader:(YGFileDownloader *)downloader downloadSize:(unsigned long long)downloadSize totalSize:(unsigned long long)totalSize;
-(void)fileDownloaderDidFinishLoad;
-(void)fileDownloaderFailWithError:(NSError *)error;
@end
@interface YGFileDownloader : NSObject
@property (nonatomic,weak) id<YGFileDownloaderDelegate> delegate;
+(instancetype)sharedInstance;
-(void)start:(NSString *)urlStr;
-(void)stop;
@end
#import "YGFileDownloader.h"
#import "NSString+util.h"
@interface YGFileDownloader () <NSURLConnectionDataDelegate>
@property (nonatomic,strong) NSURLConnection *connection;
@property (nonatomic,copy) NSString *downloadDocPath;//这个路径标识我们下载的文件所存储的那个目录,他是写死的,每次下载东西都能放在目录下面
@property (nonatomic,strong) NSFileHandle *fileHandle;
@property (nonatomic) unsigned long long downloadFileSize;//当前下载了多少(字节)
@property (nonatomic) unsigned long long totalFileSize;//文件总大小
@end
@implementation YGFileDownloader
-(NSString *)downloadDocPath{
if (_downloadDocPath==nil) {
//找到沙盒的Document目录
NSString *documentPath=[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//上面这句代码和 [NSHomeDirectory()stringByAppendingPathComponent:@"Documents"];一样
NSString *downloadPath=[documentPath stringByAppendingPathComponent:@"YGFileDownload"];
BOOL isSuccess=[[NSFileManager defaultManager] createDirectoryAtPath:downloadPath withIntermediateDirectories:YES attributes:nil error:nil];
if (isSuccess) {
NSLog(@"创建文件夹成功");
}
_downloadDocPath=downloadPath;
}
return _downloadDocPath;
}
+(instancetype)sharedInstance{//创建单例对象
static YGFileDownloader *downloader=nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{//大括号里的代码只会执行一次
downloader=[[YGFileDownloader alloc] init];
});
return downloader;
}
-(void)start:(NSString *)urlStr{
NSString *fullPath=[self prepareStart:urlStr];
//获取fullPath标识的文件的大小
_downloadFileSize=[self readDownloadFileSize:fullPath];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlStr]];
[request addValue:[NSString stringWithFormat:@"bytes=%llu-",_downloadFileSize] forHTTPHeaderField:@"Range"];//告诉服务器请求_downloadFileSize以后的字节
self.connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
-(NSString *)prepareStart:(NSString *)urlStr{//确保urlStr所对应的沙盒的位置上有文件
NSString *fileName=urlStr.md5;//把url所对应的MD5值作为文件名
//fullPath代表这个文件具体的位置(路径)
NSString *fullPath=[self.downloadDocPath stringByAppendingString:fileName];
//如果指定的这个路径下面不存在这个文件,我们就把他创建出来
BOOL fileExists=[[NSFileManager defaultManager] fileExistsAtPath:fullPath];
if (!fileExists) {
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
}
//打开这个文件准备往里面写数据
self.fileHandle=[NSFileHandle fileHandleForWritingAtPath:fullPath];
return fullPath;
//这个函数的功能是在准备下载文件之前做一些准备工作:1.确保文件存在,2.打开文件准备往里面写数据,3.获取这个文件的确切路径,因为我们在往这个路径所标识的这个文件中写数据(我们下载的数据)
}
-(unsigned long long)readDownloadFileSize:(NSString *)filePath{//读取filePath文件的文件大小
NSDictionary *fileInfo=[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
return [fileInfo fileSize];
}
-(void)stop{
[_connection cancel],_connection=nil;
[_fileHandle closeFile],_fileHandle=nil;
}
#pragma mark - <NSURLConnectionDataDelegate>
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
_totalFileSize=_downloadFileSize+response.expectedContentLength;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
if ([self.delegate conformsToProtocol:@protocol(YGFileDownloaderDelegate)]&&[self.delegate respondsToSelector:@selector(fileDownloaderFailWithError:)]) {
[self.delegate fileDownloaderFailWithError:error];
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
if ([self.delegate respondsToSelector:@selector(fileDownloaderDidFinishLoad)]) {
[self.delegate fileDownloaderDidFinishLoad];
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[_fileHandle seekToEndOfFile];//设置在末尾追加数据
[_fileHandle writeData:data];//写入数据
_downloadFileSize += data.length;
if ([self.delegate conformsToProtocol:@protocol(YGFileDownloaderDelegate)] && [self.delegate respondsToSelector:@selector(fileDownloader:downloadSize:totalSize:)]) {
[self.delegate fileDownloader:self downloadSize:_downloadFileSize totalSize:_totalFileSize];
}
}
@end