+(void)downloadFileWithOption:(NSDictionary *)paramDic requestURL:(NSString*)requestURL savedPath:(NSString*)savedPath progress:(void (^)(float progress))progress downloadSuccess:(void (^)(id responseObject))success downloadFailure:(void (^)(NSError *error))failure;
.m文件实现+(void)downloadFileWithOption:(NSDictionary *)paramDic requestURL:(NSString*)requestURL savedPath:(NSString*)savedPath progress:(void (^)(float progress))progress downloadSuccess:(void (^)(id responseObject))success downloadFailure:(void (^)(NSError *error))failure
{
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializerserializer];
NSMutableURLRequest *request =[serializerrequestWithMethod:@"POST"URLString:requestURLparameters:paramDicerror:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperationalloc]initWithRequest:request];
[operation setOutputStream:[NSOutputStreamoutputStreamToFileAtPath:savedPathappend:NO]];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead,longlong totalBytesRead,longlong totalBytesExpectedToRead) {
float p = (float)totalBytesRead / totalBytesExpectedToRead;
progress(p);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation,id responseObject) {
success(responseObject);
} failure:^(AFHTTPRequestOperation *operation,NSError *error) {
failure(error);
}];
[operation start];
}
使用
NSString *url =@"http://139.129……………………………………";
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDirectory = [pathsobjectAtIndex:0];
NSString* path = [documentsDirectorystringByAppendingPathComponent:@"test.bin"];
[UrlConnectiondownloadFileWithOption:nil
requestURL:url
savedPath:path
progress:^(float progress) {
NSLog(@"当前下载 %f",progress);
}
downloadSuccess:^(id responseObject){
NSLog(@"下载成功");
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDirectory = [pathsobjectAtIndex:0];
NSString* path = [documentsDirectorystringByAppendingPathComponent:@"test.bin"];
NSMutableData *file = [NSMutableData dataWithContentsOfFile:path];
NSLog(@"-----%lu",(unsignedlong)file.length);
}
downloadFailure:^(NSError *error)
{
}];
简单思路:通过重组progressBlock , successBlock ,requestUrl ,outPutStream,然后利用AFNetworking自带的pause,resume即可妥妥的实现。
不过碰到了一些问题也挺折磨人的,文件输入流,初始化就碰到问题了,
1.[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:YES]
如果追加了原始文件的data,就无法在下载过程中从outPutStream中获取到data。
[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:NO]
如果不追加,续传就成覆盖了。
官方文档也没仔细看,所幸就使用不追加的方式,然后手动同步文件与输入流。 方法如下
-(void)readCacheToOutStreamWithPath:(NSString*)path;
2.pause之后,AFHTTPRequestOperation.totalBytesRead,仍然记录这之前的读取长度,如果resume了,这是content-length发生了变法,这个时候就需要从组progressBlock了,同时也需要将totalBytesRead设为0,因为是私有属性,所以就用KVC。
#define Vedio @"http://221.228.249.82/youku/697A5CA0CEB3582FB91C4E3A88/03002001004E644FA2997704E9D2A7BA1E7B9D-6CAB-79A9-E635-3B92A92B3500.mp4"
#define Picture @"http://x1.zhuti.com/down/2012/11/29-win7/3D-1.jpg"
- (IBAction)download:(id)sender
{
NSString* path = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/temp"];
NSLog(@"path = %@",path);
operation = [[DownLoadOperationalloc] init];
[operationdownloadWithUrl:Picture
cachePath:^NSString *{
return path;
} progressBlock:^(NSUInteger bytesRead,long long totalBytesRead,long long totalBytesExpectedToRead) {
NSLog(@"bytesRead = %u ,totalBytesRead = %llu totalBytesExpectedToRead = %llu",bytesRead,totalBytesRead,totalBytesExpectedToRead);
float progress = totalBytesRead / (float)totalBytesExpectedToRead;
[self.progressViewsetProgress:progressanimated:YES];
[self.labelsetText:[NSStringstringWithFormat:@"%.2f%%",progress*100]];
UIImage* image = [UIImageimageWithData:operation.requestOperation.responseData];
[self.imageViewsetImage:image];
} success:^(AFHTTPRequestOperation *operation,id responseObject) {
NSLog(@"success");
// UIImage* image = [UIImage imageWithData:operation.responseData];
// [self.imageView setImage:image];
} failure:^(AFHTTPRequestOperation *operation,NSError *error) {
NSLog(@"error = %@",error);
}];
}
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
@interface DownLoadOperation :NSObject
@property(nonatomic ,strong) NSURL* url;
@property(nonatomic ,copy) NSString* (^cachePath)(void);
@property(nonatomic ,strong) AFHTTPRequestOperation* requestOperation;
@property(nonatomic ,copy) void(^progressBlock)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead);
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure;
@end
#import "DownLoadOperation.h"
@implementation DownLoadOperation
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure
{
self.cachePath = cacheBlock;
//获取缓存的长度
longlong cacheLength = [[selfclass] cacheFileWithPath:self.cachePath()];
NSLog(@"cacheLength = %llu",cacheLength);
//获取请求
NSMutableURLRequest* request = [[selfclass] requestWithUrl:urlRange:cacheLength];
self.requestOperation = [[AFHTTPRequestOperationalloc] initWithRequest:request];
[self.requestOperationsetOutputStream:[NSOutputStreamoutputStreamToFileAtPath:self.cachePath()append:NO]];
//处理流
[selfreadCacheToOutStreamWithPath:self.cachePath()];
[self.requestOperationaddObserver:selfforKeyPath:@"isPaused"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];
//获取进度块
self.progressBlock = progressBlock;
//重组进度block
[self.requestOperationsetDownloadProgressBlock:[selfgetNewProgressBlockWithCacheLength:cacheLength]];
//获取成功回调块
void (^newSuccess)(AFHTTPRequestOperation *operation,id responseObject) = ^(AFHTTPRequestOperation *operation,idresponseObject){
NSLog(@"responseHead = %@",[operation.responseallHeaderFields]);
success(operation,responseObject);
};
[self.requestOperationsetCompletionBlockWithSuccess:newSuccess
failure:failure];
[self.requestOperationstart];
}
#pragma mark - 获取本地缓存的字节
+(longlong)cacheFileWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandlefileHandleForReadingAtPath:path];
NSData* contentData = [fhreadDataToEndOfFile];
return contentData ? contentData.length :0;
}
#pragma mark - 重组进度块
-(void(^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))getNewProgressBlockWithCacheLength:(longlong)cachLength
{
typeof(self)newSelf =self;
void(^newProgressBlock)(NSUInteger bytesRead,long long totalBytesRead,long long totalBytesExpectedToRead) = ^(NSUInteger bytesRead,long long totalBytesRead,long long totalBytesExpectedToRead)
{
NSData* data = [NSDatadataWithContentsOfFile:self.cachePath()];
[self.requestOperationsetValue:dataforKey:@"responseData"];
// self.requestOperation.responseData = ;
newSelf.progressBlock(bytesRead,totalBytesRead + cachLength,totalBytesExpectedToRead + cachLength);
};
return newProgressBlock;
}
#pragma mark - 读取本地缓存入流
-(void)readCacheToOutStreamWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandlefileHandleForReadingAtPath:path];
NSData* currentData = [fhreadDataToEndOfFile];
if (currentData.length) {
//打开流,写入data,未打卡查看 streamCode = NSStreamStatusNotOpen
[self.requestOperation.outputStreamopen];
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
NSInteger dataLength = [currentDatalength];
constuint8_t * dataBytes = [currentDatabytes];
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.requestOperation.outputStreamwrite:&dataBytes[bytesWrittenSoFar]maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten !=0);
if (bytesWritten == -1) {
break;
} else {
bytesWrittenSoFar += bytesWritten;
}
} while (bytesWrittenSoFar != dataLength);
}
}
#pragma mark - 获取请求
+(NSMutableURLRequest*)requestWithUrl:(id)url Range:(longlong)length
{
NSURL* requestUrl = [urlisKindOfClass:[NSURLclass]] ? url : [NSURLURLWithString:url];
NSMutableURLRequest* request = [NSMutableURLRequestrequestWithURL:requestUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5*60];
if (length) {
[request setValue:[NSStringstringWithFormat:@"bytes=%lld-",length]forHTTPHeaderField:@"Range"];
}
NSLog(@"request.head = %@",request.allHTTPHeaderFields);
return request;
}
#pragma mark - 监听暂停
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context
{
NSLog(@"keypath = %@ changeDic = %@",keyPath,change);
//暂停状态
if ([keyPathisEqualToString:@"isPaused"] && [[changeobjectForKey:@"new"]intValue] ==1) {
longlong cacheLength = [[selfclass] cacheFileWithPath:self.cachePath()];
//暂停读取data从文件中获取到NSNumber
cacheLength = [[self.requestOperation.outputStreampropertyForKey:NSStreamFileCurrentOffsetKey]unsignedLongLongValue];
NSLog(@"cacheLength = %lld",cacheLength);
[self.requestOperationsetValue:@"0"forKey:@"totalBytesRead"];
//重组进度block
[self.requestOperationsetDownloadProgressBlock:[selfgetNewProgressBlockWithCacheLength:cacheLength]];
}
}
@end
2.上传文件
+ (void)uploadFileWithOption:(NSDictionary *)paramDic requestURL:(NSString*)requestURL fileData:(NSData*)fileData progress:(void (^)(float progress))progress uploadSuccess:(void (^)(id responseObject))success uploadFailure:(void (^)(NSError *error))failure
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *operation = [manager POST:requestURL parameters:paramDic constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:fileData name:@"file" fileName:@"test.bin" mimeType:@""];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
success(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten,longlong totalBytesExpectedToWrite) {
float p = (float)totalBytesWritten / totalBytesExpectedToWrite;
progress(p);
}];
}
************************************************************************************************************************直接用NSURLConnection下载
#import "ViewController.h"
#import "DACircularProgressView.h"
/**
* NSURLConnectionDataDelegate协议中的代理方法
开始接收到服务器的响应时调用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
接收到服务器返回的数据时调用(服务器返回的数据比较大时会调用多次)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
服务器返回的数据完全接收完毕后调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
请求出错时调用(比如请求超时)
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
*/
@interface ViewController () <NSURLConnectionDataDelegate>
/**
* 用来写数据的文件句柄对象
*/
@property (nonatomic,strong)NSFileHandle *writeHandle;
/**
* 文件的总大小
*/
@property (nonatomic,assign)longlong totalLength;
/**
* 当前已经写入的文件大小
*/
@property (nonatomic,assign)longlong currentLength;
@property (nonatomic,weak)DACircularProgressView *circleView;//用这个类来添加下载进度条
@end
@implementation HMViewController
- (void)viewDidLoad
{
[superviewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
/**
* 用DACircularProgress这个类添加下载进度条
*/
DACircularProgressView *circleView = [[DACircularProgressViewalloc]init];
circleView.frame = CGRectMake(100, 50, 100, 100);
circleView.progressTintColor = [UIColorredColor];
circleView.trackTintColor = [UIColorblueColor];
circleView.progress = 0.01;
[self.viewaddSubview:circleView];
self.circleView = circleView;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.URL
NSURL *url = [NSURLURLWithString:@"http://hd.shijue.cvidea.cn/tf/140422/2330218/5355ca2
f3dfae93acb000001.JPEG"];
// 2.请求
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 3.下载(创建完conn对象后,会自动发起一个异步请求)
[NSURLConnectionconnectionWithRequest:requestdelegate:self];
}
#pragma mark - NSURLConnectionDataDelegate代理方法
/**
* 请求失败时调用(请求超时、网络异常)
*
* @param error 错误原因
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"didFailWithError");
}
/**
* 1.接收到服务器的响应就会调用 (先搞一个0kb的文件,然后用writeHandle关联那个文件,最后写入数据
* @param response 响应
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// 文件路径 沙盒中的caches的路径
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject];
NSLog(@"caches = %@",caches);
//用这个stringByAppendingPathComponent:方法会自动添加一个/表示这是个路径
NSString *filepath = [cachesstringByAppendingPathComponent:@"photodownload.JPEG"];//先搞一个0kb的文件
// 创建一个空的文件到沙盒中
NSFileManager *mgr = [NSFileManagerdefaultManager];
[mgr createFileAtPath:filepathcontents:nilattributes:nil];
// 创建一个用来写数据的文件句柄
self.writeHandle = [NSFileHandlefileHandleForWritingAtPath:filepath];//然后用writeHandle关联那个文件
// 获得文件的总大小
self.totalLength = response.expectedContentLength;
}
/**
* 2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
*
* @param data 这次返回的数据
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data
{
// 移动到文件的最后面
[self.writeHandleseekToEndOfFile];
// 将数据写入沙盒(先移动到最后面再拼接)
[self.writeHandlewriteData:data]; //最后写入数据
// 累计文件的长度
self.currentLength += data.length;
NSLog(@"下载进度:%f", (double)self.currentLength/self.totalLength);
self.circleView.progress = (double)self.currentLength/self.totalLength;
}
/**
* 3.加载完毕后调用(服务器的数据已经完全返回后)
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.currentLength =0;
self.totalLength =0;
// 关闭文件
[self.writeHandlecloseFile];
self.writeHandle =nil;
}