作者:Mitchell
一、文件下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,strong)NSString * path;
@property(nonatomic,strong)NSFileHandle * handle;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//小文件的下载可以直接写入 NSData,对内存的影响不大,但是大文件必须写入磁盘,不然会造成内存暴增。
NSURL*url = [NSURL URLWithString:nil];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"%s",__func__);
self.path = [response.suggestedFilename cacheDir];
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager createFileAtPath:self.path contents:nil attributes:nil]){
NSLog(@"成功");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(@"%s",__func__);
[self.handle seekToEndOfFile];
[self.handle writeData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"%s",__func__);
[self.handle closeFile];
self.handle = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"%@",error);
}
-(NSFileHandle *)handle{
if (!_handle) {
_handle = [NSFileHandle fileHandleForWritingAtPath:self.path];
}
return _handle;
}
@end
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,strong)NSOutputStream * stream;
@property(nonatomic,strong)NSString * path;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL*url = nil;
NSURLRequest*request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
self.path = [response.suggestedFilename cacheDir];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[_stream write:data.bytes maxLength:data.length];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[_stream close];
_stream = nil;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"%@",error);
}
-(NSOutputStream *)stream{
if (!_stream) {
_stream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];
[_stream open];
}
return _stream;
}
@end
二、单线程文件断点下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
#define URL @"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,strong)NSString * path;
@property(nonatomic,strong)NSOutputStream * stream;
@property(nonatomic,weak)IBOutlet UIProgressView * progress;
@property(nonatomic,assign)CGFloat currentData;
@property(nonatomic,assign)CGFloat totalData;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1、初始化路径
self.path = [@"abc.mp4" cacheDir];
NSLog(@"%@",_path);
/*2、发送网络请求
获取当前文件的大小,设置下载的范围
*/
NSString*strUrl = URL;
NSURL*url = [NSURL URLWithString:strUrl];
NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
/*
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
*/
//total:48427665
//0.764996
self.currentData = [self getFileSize:self.path];
NSLog(@"%zd", [self getFileSize:self.path]);
NSString*range = [NSString stringWithFormat:@"bytes=%zd-",[self getFileSize:self.path]];
[request setValue:range forHTTPHeaderField:@"Range"];
[NSURLConnection connectionWithRequest:request delegate:self];
}
#pragma mark ------------------ 接收response ------------------
//计算文件的总大小
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
//文件的总大小 = 当前请求的总大小 + 已经下载的文件的大小
self.totalData = response.expectedContentLength + _currentData;
}
#pragma mark ------------------ 接收数据 ------------------
//不断获取数据,添加当前的下载进度,改变进度条
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
_currentData += data.length;
self.progress.progress = _currentData/_totalData * 1.0;
[self.stream write:data.bytes maxLength:data.length];
}
#pragma mark ------------------ 下载失败 ------------------
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"%@",error);
}
#pragma mark ------------------ 下载完成 ------------------
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"%s",__func__);
//关闭流
[_stream close];
_stream = nil;
}
#pragma mark ------------------ 懒加载NSOutputStream ------------------
-(NSOutputStream *)stream{
if (!_stream) {
_stream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];
[_stream open];
}
return _stream;
}
#pragma mark ------------------ 获取当前文件的大小 ------------------
-(NSInteger)getFileSize:(NSString*)path{
NSFileManager*manager = [NSFileManager defaultManager];
NSDictionary*dict = [manager attributesOfItemAtPath:path error:nil];
return [dict[NSFileSize] integerValue];
}
@end
三、文件的压缩与解压
- 这里是用 ZipArchive 第三方库实现,暂时不支持CocoaPods,需要手动添加,并添加
libz
框架,使用方法如下
#import "ViewController.h"
#import "Main.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//压缩文件
/*创建压缩文件存放路径*/
NSString*saveStr = nil;
NSArray*arr = @[@"文件路径1",@"文件路径2"];
if ([Main createZipFileAtPath:saveStr withFilesAtPaths:arr]) {
NSLog(@"压缩成功");
} ;
[Main createZipFileAtPath:saveStr withContentsOfDirectory:@"哪个文件?"];
//解压文件
[Main unzipFileAtPath:@"现在的文件" toDestination:@"解压到哪个文件夹"];
}
@end
四、文件的上传
- 文件的上传需要遵守非常严格的格式,差一点都不可以上传成功,具体实现如下
#import "ViewController.h"
/*
文件上传格式:
- 请求头
+ multipart/form-data 代表是需要上传文件
+ ----WebKitFormBoundaryN2S2xc0l0oWUaYie 分隔符, 可以是任意字符串,注意前面是四个----
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryN2S2xc0l0oWUaYie
- 请求体:
+ 请求体中的分隔符必须比请求头中的分隔符多两个--
+ 分隔符必须和请求头中的一样
分隔符
\n
Content-Disposition: form-data; name="file"; filename=“videos.plist"
\n
Content-Type: application/octet-stream
\n
\n
文件数据
\n
\n
分隔符
\n
Content-Disposition: form-data; name=“username"
\n
\n
非文件数据
\n
结束符号
*/
// 请求头的
#define MitchellHeaderBoundary @"----xiaomage"
// 请求体的
#define MitchellBoundary [@"------xiaomage" dataUsingEncoding:NSUTF8StringEncoding]
// 结束符
#define MitchellEndBoundary [@"------xiaomage--" dataUsingEncoding:NSUTF8StringEncoding]
// 将字符串转换为二进制
#define MitchellEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]
// 换行
#define MitchellNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()
@end
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.创建URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
// 2.根据URL创建NSURLRequest
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2.2设置请求体
NSMutableData *data = [NSMutableData data];
// 2.2.1设置文件参数
[data appendData:MitchellBoundary];
[data appendData:MitchellNewLine];
// name : 对应服务端接收的字段类型(服务端参数的名称)
// filename: 告诉服务端当前的文件的文件名称(也就是告诉服务器用什么名称保存当前上传的文件)
[data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"videos.plist\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:[@"Content-Type: application/octet-stream" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
UIImage *image = [UIImage imageNamed:@"abc"];
NSData *imageData = UIImagePNGRepresentation(image);
[data appendData:imageData];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
// 2.2.2设置非文件参数
[data appendData:MitchellBoundary];
[data appendData:MitchellNewLine];
// name : 对应服务端接收的字段类型(服务端参数的名称)
[data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
[data appendData:[@"lnj" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
// 2.2.3设置结束符号
[data appendData:MitchellEndBoundary];
// [data appendData:MitchellNewLine];
request.HTTPBody = data;
// 2.1设置请求头
// 注意: 设置请求头的字典不需要:
request.HTTPMethod = @"POST";
NSString *temp = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", MitchellHeaderBoundary];
[request setValue:temp forHTTPHeaderField:@"Content-Type"];
// 3.利用NSURLConnetion发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
}
@end
五、关于 MIME
- 互联网邮件扩展类型,用于标记文件的方式类型,这里有具体介绍
- Content-Type 中传递的就是 MIME 的类型
- 获取文件的 MIME
创建一个本地链接从 response 中获取即可。
- (NSString *)MIMEType{
NSURL*url = [NSURL fileURLWithPath:self];
NSURLRequest *reqeust = [NSURLRequest requestWithURL:url];
NSURLResponse*response = nil;
[NSURLConnection sendSynchronousRequest:reqeust returningResponse:&response error:nil];
return response.MIMEType;
}