iOS - 文件操作

作者:Mitchell 

一、文件下载

  • NSHandler 句柄
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()
@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
  • NSOutputStream 流下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()
@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 ()
@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;
}

你可能感兴趣的:(iOS - 文件操作)