一、小文件下载
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { UIImage *image = [UIImage imageWithData:data]; }];
二、大文件下载
1、利用NSMutableData,缺点是会导致内存暴涨(以下用fileData,也就是注释部分)
2、利用NSFileHandle,该类就是专门用于操作文件(handle)
@interface ViewController ()<NSURLConnectionDataDelegate> @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (nonatomic, assign)NSUInteger currentLength; /**< 当前已经接收的长度 */ @property (nonatomic, assign)NSUInteger totalLength; /**< 需要接收的总长度 */ @property (nonatomic, strong)NSMutableData *fileData; /**< 用于存储下载的文件 */ @property (nonatomic, strong) NSFileHandle *handle; /**< 文件句柄 */ @property (nonatomic, copy) NSString *path; /**< 保存文件的路径 */ @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark - NSURLConnectionDataDelegate // 接收到服务器的响应 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // 根据文件名称, 拼接保存文件的路径 self.path = [response.suggestedFilename cacheDir]; // 保存文件的总大小 self.totalLength = response.expectedContentLength; // 创建一个空的文件, 用于存储下载的数据 NSFileManager *manager = [NSFileManager defaultManager]; if([manager createFileAtPath:self.path contents:nil attributes:nil]){ NSLog(@"创建空文件成功"); } } // 接收到服务器的数据调用(会调用一次或多次) - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 将每次下载的内容存储到fileData中 // 会导致内存暴涨 // [self.fileData appendData:data]; // 告诉handle从什么地方开始写入 // 从没有数据的地方开始写入, 也就是在上一次的后面拼接 [self.handle seekToEndOfFile]; // 写入数据 [self.handle writeData:data]; // 计算进度 self.currentLength += data.length; self.progressView.progress = 1.0 * self.currentLength / self.totalLength; } // 接收完毕时调用 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // [self.fileData writeToFile:@"/Users/xiaomage/Desktop/abc.mp4" atomically:YES]; // 关闭文件句柄 [self.handle closeFile]; self.handle = nil; } // 接收发生错误时调用 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { } #pragma mark - lazy - (NSMutableData *)fileData { if (!_fileData) { _fileData = [NSMutableData data]; } return _fileData; } - (NSFileHandle *)handle { if (!_handle) { // NSFileHandle类就是专门用于操作文件 _handle = [NSFileHandle fileHandleForWritingAtPath:self.path]; } return _handle; } @end
3、使用NSOutputStream(输出流)
@interface ViewController ()<NSURLConnectionDataDelegate> @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (nonatomic, assign)NSUInteger currentLength; /**< 当前已经接收的长度 */ @property (nonatomic, assign)NSUInteger totalLength; /**< 需要接收的总长度 */ @property (nonatomic, copy) NSString *path; /**< 保存文件的路径 */ @property (nonatomic, strong) NSOutputStream *outputStream ; /**< 输出流 */ @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark - NSURLConnectionDataDelegate // 接收到服务器的响应 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // 根据文件名称, 拼接保存文件的路径 self.path = [response.suggestedFilename cacheDir]; NSLog(@"%@", self.path); // 保存文件的总大小 self.totalLength = response.expectedContentLength; NSLog(@"%zd", response.expectedContentLength); } // 接收到服务器的数据调用(会调用一次或多次) - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 直接写入数据 /* 第一个参数: 需要写入的数据 第二个参数: 写入数据的大小 */ [self.outputStream write:data.bytes maxLength:data.length]; // 计算进度 self.currentLength += data.length; self.progressView.progress = 1.0 * self.currentLength / self.totalLength; } // 接收完毕时调用 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // 关闭输出流 [self.outputStream close]; self.outputStream = nil; } // 接收发生错误时调用 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { } - (NSOutputStream *)outputStream { if (!_outputStream) { /* 第一个参数: 告诉系统数据流需要输出到哪个文件中 第二个参数: 如果传入YES, 代表每次都在上一次的后面追加 如果传入NO, 代表每次都从头开始 */ _outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES]; // 注意: 如果想利用输出流写入数据, 一定要打开数据流 // 如果数据流打开的文件不存在, 那么会自动创建个新的 [_outputStream open]; } return _outputStream; } @end
三、文件压缩/解压缩(使用三方框架ZipArchive)
在ZipArchive框架中有一下几个方法可快速实现文件压缩和解压缩
// 第一个参数: 压缩后的文件保存到什么地方(zip文件) // 第二个参数: 哪些文件需要压缩(传入一个存了文件名的数组) + (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths; // 第一个参数: 压缩后的文件保存到什么地方(zip文件) // 第二个参数: 哪个文件夹的文件需要压缩(传入你想压缩的文件名) + (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath; // 第一个参数:需要解压的文件 // 第二个参数:解压到什么地方(传入解压缩的目的文件名) + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination;
四、MIMEType(获取文件类型)
文件的万能类型 application/octet-stream
1、利用NSURLConnection
- (NSString *)MIMEType:(NSURL *)url { // 1.创建一个请求 NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 2.发送请求(返回响应) NSURLResponse *response = nil; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]; // 3.获得MIMEType return response.MIMEType; }
2、C语言API
+ (NSString *)mimeTypeForFileAtPath:(NSString *)path { if (![[NSFileManager alloc] init] fileExistsAtPath:path]) { return nil; } CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL); CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType); CFRelease(UTI); if (!MIMEType) { return @"application/octet-stream"; } return NSMakeCollectable(MIMEType); }
五、文件的上传
1、如何设置请求头
[request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];
2、如何设置请求体
+ 文件参数
--分割线
Content-Disposition: form-data; name="参数名"; filename="文件名"
Content-Type: 文件的MIMEType
文件数据
+ 非文件参数
--分割线
Content-Disposition: form-data; name="参数名"
参数值
+ 结束标记
参数结束的标记
--分割线--
- 注意事项
+ `请求体`比`请求头`分割线**前面**`多两个--`
+ `结束标记`比`请求体`**后面**`多两个--`
3、上传步骤:
1.创建URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
2.根据URL创建NSURLRequest
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
2.1设置请求头
request.HTTPMethod = @"POST"; [request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];
2.2设置请求体
NSMutableData *data = [NSMutableData data];
2.2.1设置文件参数
2.2.2设置非文件参数
2.2.3设置结束符号
request.HTTPBody = data;
3.利用NSURLConnetion发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
}];
六、断点下载(续传)
NSUrlConnection实现断点续传的关键是自定义http request的头部的range域属性。
Range头域
Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。
NSURL *url1=[NSURL URLWithString:@"下载地址"; NSMutableURLRequest* request1=[NSMutableURLRequest requestWithURL:url1]; [request1 setValue:@"bytes=20000-" forHTTPHeaderField:@"Range"]; [request1 setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; NSData *returnData1 = [NSURLConnection sendSynchronousRequest:request1 returningResponse:nil error:nil]; [self writeToFile:returnData1 fileName:@"SOMEPATH"]; -(void)writeToFile:(NSData *)data fileName:(NSString *) fileName { NSString *filePath=[NSString stringWithFormat:@"%@",fileName]; if([[NSFileManager defaultManager] fileExistsAtPath:filePath] == NO){ NSLog(@"file not exist,create it..."); [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; }else { NSLog(@"file exist!!!"); } FILE *file = fopen([fileName UTF8String], [@"ab+" UTF8String]); if(file != NULL){ fseek(file, 0, SEEK_END); } int readSize = [data length]; fwrite((const void *)[data bytes], readSize, 1, file); fclose(file); }