iOS的文件上传与下载

iOS中用系统提供的API能实现能实现文件的上传与下载,分别有两种方式。NSURLConnectionNSURLSession

其中NSURLConnection是使用很久的的一种方式,NSURLSession是新出来的一种方式。

文件上传

一、 POST方式上传

POST方式提交信息默认使用的是  :
* Content-Type :    application/x-www-form-urlencoded .
* 输入中文时 , post 方式自动进行转义 ( 苹果中自动 ).

国内的绝大多数网站都采用这种方式 上传文件 ( 支持二进制文件 )
* Content-Type : multipart/form-data ( 上传文件 )
* 都会限制上传文件的大小 一般是 2M 或者更小。


在苹果中进行上传操作十分麻烦。需要拼接好上传所需要的字符串格式,然后才能实现上传。(还要加上头部)


其他平台做的好一点的可能封装好了,不需要自己拼接字符串格式。因此iOS中很少用这种方式上传。但是AFNetworking很好地封装了POST请求,也很好用。

示例代码:

[objc]  view plain copy
  1. #import "XNUploadFile.h"  
  2.   
  3. #define kTimeOut 5.0f  
  4.   
  5. @implementation XNUploadFile  
  6. /** 分隔字符串 */  
  7. static NSString *boundaryStr = @"--";  
  8. /** 本次上传标示字符串 */  
  9. static NSString *randomIDStr;  
  10. /** 上传(php)脚本中,接收文件字段 */  
  11. static NSString *uploadID;  
  12.   
  13. - (instancetype)init  
  14. {  
  15.     self = [super init];  
  16.     if (self) {  
  17.         /** 本次上传标示字符串 */  
  18.         randomIDStr = @"itcastupload";  
  19.         /** 上传(php)脚本中,接收文件字段 */  
  20.         // 可以咨询公司的网站开发程序员  
  21.         // 或者用FireBug自己跟踪调试  
  22.         uploadID = @"uploadFile";  
  23.     }  
  24.     return self;  
  25. }  
  26.   
  27. #pragma mark - 成员方法.  用NSURLSession来完成上传  
  28. - (void)uploadFile:(NSString *)path fileName:(NSString *)fileName completion:(void (^)(NSString *string))completion  
  29. {  
  30.     // 1. url 提示:真正负责文件上传的是php文件,而不是html文件  
  31.     NSURL *url = [NSURL URLWithString:@"http://localhost/new/post/upload.php"];  
  32.       
  33.     // 2. request  
  34.     NSURLRequest *request = [self requestForUploadURL:url uploadFileName:fileName localFilePath:path];  
  35.       
  36.     // 3. session(回话)  
  37.     // 全局网络回话,为了方便程序员使用网络服务  
  38.     NSURLSession *session = [NSURLSession sharedSession];  
  39.       
  40.     // 4. 数据任务-> 任务都是由回话发起的  
  41.     /** URLSession的任务,默认都是在其他线程工作的,默认都是异步的 */  
  42.     [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {  
  43.           
  44.         id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];  
  45.           
  46.         NSLog(@"%@ %@", result, [NSThread currentThread]);  
  47.           
  48.         dispatch_async(dispatch_get_main_queue(), ^{  
  49.             if (completion) {  
  50.                 completion(@"下载完成");  
  51.             }  
  52.         });  
  53.     }] resume];  
  54.       
  55.     //    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {  
  56.     //  
  57.     //        id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];  
  58.     //  
  59.     //        NSLog(@"%@ %@", result, [NSThread currentThread]);  
  60.     //  
  61.     //        dispatch_async(dispatch_get_main_queue(), ^{  
  62.     //            if (completion) {  
  63.     //                completion(@"下载完成");  
  64.     //            }  
  65.     //        });  
  66.     //    }];  
  67.     //      
  68.     //    // 5. 启动任务  
  69.     //    [task resume];  
  70. }  
  71.   
  72. #pragma mark - 私有方法 : 拼字符串  
  73. /** 拼接顶部字符串 */  
  74. - (NSString *)topStringWithMimeType:(NSString *)mimeType uploadFile:(NSString *)uploadFile  
  75. {  
  76.     NSMutableString *strM = [NSMutableString string];  
  77.       
  78.     [strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr];  
  79.     [strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\n", uploadID, uploadFile];  
  80.     [strM appendFormat:@"Content-Type: %@\n\n", mimeType];  
  81.       
  82.     NSLog(@"顶部字符串:%@", strM);  
  83.     return [strM copy];  
  84. }  
  85.   
  86. /** 拼接底部字符串 */  
  87. - (NSString *)bottomString  
  88. {  
  89.     NSMutableString *strM = [NSMutableString string];  
  90.       
  91.     [strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr];  
  92.     [strM appendString:@"Content-Disposition: form-data; name=\"submit\"\n\n"];  
  93.     [strM appendString:@"Submit\n"];  
  94.     [strM appendFormat:@"%@%@--\n", boundaryStr, randomIDStr];  
  95.       
  96.     NSLog(@"底部字符串:%@", strM);  
  97.     return [strM copy];  
  98. }  
  99.   
  100. /** 指定全路径文件的mimeType */  
  101. - (NSString *)mimeTypeWithFilePath:(NSString *)filePath  
  102. {  
  103.     // 1. 判断文件是否存在  
  104.     if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {  
  105.         return nil;  
  106.     }  
  107.       
  108.     // 2. 使用HTTP HEAD方法获取上传文件信息  
  109.     NSURL *url = [NSURL fileURLWithPath:filePath];  
  110.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
  111.       
  112.     // 3. 调用同步方法获取文件的MimeType  
  113.     NSURLResponse *response = nil;  
  114.     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];  
  115.       
  116.     return response.MIMEType;  
  117. }  
  118.   
  119. /** 上传文件网络请求 */  
  120. - (NSURLRequest *)requestForUploadURL:(NSURL *)url uploadFileName:(NSString *)fileName localFilePath:(NSString *)filePath  
  121. {  
  122.     // 0. 获取上传文件的mimeType  
  123.     NSString *mimeType = [self mimeTypeWithFilePath:filePath];  
  124.     if (!mimeType) return nil;  
  125.       
  126.     // 1. 拼接要上传的数据体  
  127.     NSMutableData *dataM = [NSMutableData data];  
  128.     [dataM appendData:[[self topStringWithMimeType:mimeType uploadFile:fileName] dataUsingEncoding:NSUTF8StringEncoding]];  
  129.     // 拼接上传文件本身的二进制数据  
  130.     [dataM appendData:[NSData dataWithContentsOfFile:filePath]];  
  131.     [dataM appendData:[[self bottomString] dataUsingEncoding:NSUTF8StringEncoding]];  
  132.       
  133.     // 2. 设置请求  
  134.     NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeOut];  
  135.     // 1> 设定HTTP请求方式  
  136.     requestM.HTTPMethod = @"POST";  
  137.     // 2> 设置数据体  
  138.     requestM.HTTPBody = dataM;  
  139.     // 3> 指定Content-Type  
  140.     NSString *typeStr = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", randomIDStr];  
  141.     [requestM setValue:typeStr forHTTPHeaderField:@"Content-Type"];  
  142.     // 4> 指定数据长度  
  143.     NSString *lengthStr = [NSString stringWithFormat:@"%@", @([dataM length])];  
  144.     [requestM setValue:lengthStr forHTTPHeaderField:@"Content-Length"];  
  145.       
  146.     return [requestM copy];  
  147. }  

注意: POST 上传时 , 不允许重名 . ( 否则出错 )


二、 PUT方式上传

session 中的 upload 方法只能用于 PUT 上传 , 不能用于 POST 上传 .

PUT 方式上传的 好处 : ( 需要身份验证 )
* 不用像 POST 一样 , 拼一堆字符串 .
* 直接 base64 编码一下身份验证 session upload 一调用就行了 .
* 没有文件大小限制 .
* 即时通讯 里面用的多 . ( 发图片 / 发语音 )

[objc]  view plain copy
  1. - (void)putFile  
  2. {  
  3.     // 1. url 最后一个是要上传的文件名  
  4.     NSURL *url = [NSURL URLWithString:@"http://localhost/uploads/abcd"]; //abcd为文件名  
  5.       
  6.     // 2. request  
  7.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
  8.     request.HTTPMethod = @"PUT";  
  9. //    request.HTTPMethod = @"DELETE";  
  10.       
  11.     // 设置用户授权  
  12.     // BASE64编码:一种对字符串和二进制数据进行编码的一种“最常用的网络编码方式”,此编码可以将二进制数据转换成字符串!  
  13.     // 是很多加密算法的底层算法  
  14.     // BASE64支持反编码,是一种双向的编码方案  
  15.     NSString *authStr = @"admin:123";  
  16.     NSString *authBase64 = [NSString stringWithFormat:@"Basic %@", [self base64Encode:authStr]];  
  17.     [request setValue:authBase64 forHTTPHeaderField:@"Authorization"];  
  18.       
  19.     // 3. URLSession  
  20.     NSURLSession *session = [NSURLSession sharedSession];  
  21.       
  22.     // 4. 由session发起任务  
  23.     NSURL *localURL = [[NSBundle mainBundle] URLForResource:@"001.png" withExtension:nil];  
  24.     [[session uploadTaskWithRequest:request fromFile:localURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {  
  25.           
  26.         NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
  27.           
  28.         NSLog(@"sesult---> %@ %@", result, [NSThread currentThread]);  
  29.     }] resume];  
  30. }  
  31.   
  32. - (NSString *)base64Encode:(NSString *)str  
  33. {  
  34.     // 1. 将字符串转换成二进制数据  
  35.     NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];  
  36.       
  37.     // 2. 对二进制数据进行base64编码  
  38.     NSString *result = [data base64EncodedStringWithOptions:0];  
  39.       
  40.     NSLog(@"base464--> %@", result);  
  41.       
  42.     return result;  
  43. }  

PUT方式与DELETE对应,DELETE用于删除PUT方式上传的文件。


TIPS:session使用注意

* 网络会话 ,  方便程序员使用网络服务 .
* : 可以获得当前上传文件的进度 .
* NSURLSession 的任务 默认都是 异步 . ( 在其他线程中工作 )
* Task 是由 会话 发起的 .
* 注意网络请求都要进行出错处理 .
* session 默认是挂起的 ,  需要 resume 一下才能启动 .

文件下载

文件的下载分为NSURLConnectionNSURLSession两种,前一种有恨悠久的历史了。使用相对麻烦,后者是新出来的,增加了一些额外的功能。

一、NSURLConnection实现下载

TIPS:

1、 NSURLConnection 下载时 , 得到的 NSData 写入文件 , data 并没有 占用多大内存 ( 即使文件很大 )
2、 一点点在传 做的是磁盘缓存 . 而不是内存缓存机制。
3、了解在 NSURLConnection 上加代理。 [con setDelegateQueue :[[NSOperationQueue alloc] init]]

4、 NSURLResponse 记录的了 url, mineType, exceptedContentLength, suggestedFileName 等属性 .  下载时用得着 .  

以下程序实现追踪下载百分比的下载(URLConnection自带的方法):

[objc]  view plain copy
  1. #import "XNDownload.h"  
  2.   
  3. typedef void(^ProgressBlock)(float percent);  
  4.   
  5. @interface XNDownload() <NSURLConnectionDataDelegate>  
  6.   
  7. @property (nonatomicstrongNSMutableData *dataM;  
  8.   
  9. // 保存在沙盒中的文件路径  
  10. @property (nonatomicstrongNSString *cachePath;  
  11. // 文件总长度  
  12. @property (nonatomic, assign) long long fileLength;  
  13. // 当前下载的文件长度  
  14. @property (nonatomic, assign) long long currentLength;  
  15.   
  16. // 回调块代码  
  17. @property (nonatomiccopy) ProgressBlock progress;  
  18.   
  19. @end  
  20.   
  21. @implementation XNDownload  
  22.   
  23. - (NSMutableData *)dataM  
  24. {  
  25.     if (!_dataM) {  
  26.         _dataM = [NSMutableData data];  
  27.     }  
  28.     return _dataM;  
  29. }  
  30.   
  31. - (void)downloadWithURL:(NSURL *)url progress:(void (^)(float))progress  
  32. {  
  33.     // 0. 记录块代码  
  34.     self.progress = progress;  
  35.       
  36.     // 1. request GET  
  37.     NSURLRequest *request = [NSURLRequest requestWithURL:url];  
  38.       
  39.     // 2. connection  
  40.     NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];  
  41.       
  42.     // 让connection支持多线程,指定代理的工作队列即可  
  43.     // NSURLConnection在运行时,运行循环不负责监听代理的具体执行  
  44.     [connection setDelegateQueue:[[NSOperationQueue alloc] init]];  
  45.       
  46.     // 3. 启动连接  
  47.     [connection start];  
  48. }  
  49.   
  50. #pragma mark - 代理方法  
  51. // 1. 接收到服务器的响应,服务器执行完请求,向客户端回传数据  
  52. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response  
  53. {  
  54.     NSLog(@"%@ %lld", response.suggestedFilename, response.expectedContentLength);  
  55.     // 1. 保存的缓存路径  
  56.     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];  
  57.     self.cachePath = [cachePath stringByAppendingPathComponent:response.suggestedFilename];  
  58.     // 2. 文件总长度  
  59.     self.fileLength = response.expectedContentLength;  
  60.     // 3. 当前下载的文件长度  
  61.     self.currentLength = 0;  
  62.       
  63.     // 清空数据  
  64.     [self.dataM setData:nil];  
  65. }  
  66.   
  67. // 2. 接收数据,从服务器接收到数据  
  68. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data  
  69. {  
  70.     // 拼接数据  
  71.     [self.dataM appendData:data];  
  72.       
  73.     // 根据data的长度增加当前下载的文件长度  
  74.     self.currentLength += data.length;  
  75.       
  76.     float progress = (float)self.currentLength / self.fileLength;  
  77.       
  78.     // 判断是否定义了块代码  
  79.     if (self.progress) {  
  80.         [[NSOperationQueue mainQueue] addOperationWithBlock:^{  
  81.             // 强制运行循环执行一次更新  
  82.             [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];  
  83.               
  84.             self.progress(progress);  
  85.         }];  
  86.     }  
  87. }  
  88.   
  89. // 3. 完成接收  
  90. - (void)connectionDidFinishLoading:(NSURLConnection *)connection  
  91. {  
  92.     NSLog(@"%s %@", __func__, [NSThread currentThread]);  
  93.     // 将dataM写入沙盒的缓存目录  
  94.     // 写入数据,NSURLConnection底层实现是用磁盘做的缓存  
  95.     [self.dataM writeToFile:self.cachePath atomically:YES];  
  96. }  
  97.   
  98. // 4. 出现错误  
  99. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error  
  100. {  
  101.     NSLog(@"%@", error.localizedDescription);  
  102. }  
  103.   
  104. @end  

二、NSURLSession实现下载

NSURLSession能实现断点续传,暂停下载等功能。

1、session提供的是 开了多个线程的异步下载.
2、下载的暂停续传: (session的 代理中的方法)
*弄一个NSData变量来保存下载东西.暂停时将下载任务task清空.
* 续传:将暂停时的data交给session继续下载,并将先前的data清空.
3、 task 一定要 resume 才开始执行 .

[objc]  view plain copy
  1. #import "XNViewController.h"  
  2.   
  3. @interface XNViewController () <NSURLSessionDownloadDelegate>  
  4.   
  5. // 下载网络回话  
  6. @property (nonatomicstrongNSURLSession *session;  
  7. // 下载任务  
  8. @property (nonatomicstrongNSURLSessionDownloadTask *downloadTask;  
  9. // 续传的二进制数据  
  10. @property (nonatomicstrongNSData *resumeData;  
  11. @end  
  12.   
  13. @implementation XNViewController  
  14.   
  15. - (NSURLSession *)session  
  16. {  
  17.     if (!_session) {  
  18.         NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];  
  19.         _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];  
  20.     }  
  21.     return _session;  
  22. }  
  23.   
  24. - (void)viewDidLoad  
  25. {  
  26.     [super viewDidLoad];  
  27.       
  28.     [self downloadFile];  
  29. }  
  30.   
  31. // 暂停下载任务  
  32. - (IBAction)pause  
  33. {  
  34.     // 如果下载任务不存在,直接返回  
  35.     if (self.downloadTask == nilreturn;  
  36.       
  37.     // 暂停任务(块代码中的resumeData就是当前正在下载的二进制数据)  
  38.     // 停止下载任务时,需要保存数据  
  39.     [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {  
  40.         self.resumeData = resumeData;  
  41.           
  42.         // 清空并且释放当前的下载任务  
  43.         self.downloadTask = nil;  
  44.     }];  
  45. }  
  46.   
  47. - (IBAction)resume  
  48. {  
  49.     // 要续传的数据是否存在?  
  50.     if (self.resumeData == nilreturn;  
  51.       
  52.     // 建立续传的下载任务  
  53.     self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];  
  54.     [self.downloadTask resume];  
  55.       
  56.     // 将此前记录的续传数据清空  
  57.     self.resumeData = nil;  
  58. }  
  59.   
  60. // 如果在开发中使用到缓存目录,一定要提供一个功能,“清除缓存”!  
  61. /** 下载文件 */  
  62. - (void)downloadFile  
  63. {  
  64.     NSString *urlStr = @"http://localhost/苍老师全集.rmvb";  
  65.     urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];  
  66.       
  67.     NSURL *url = [NSURL URLWithString:urlStr];  
  68.   
  69.     // (1) 代理 & 直接启动任  
  70.     // 2. 启动下载任务  
  71.     self.downloadTask = [self.session downloadTaskWithURL:url];  
  72.       
  73.     [self.downloadTask resume];  
  74. }  
  75.   
  76. #pragma mark - 下载代理方法  
  77. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location  
  78. {  
  79.     NSLog(@"完成 %@ %@", location, [NSThread currentThread]);  
  80. }  
  81.   
  82. /** 
  83.  bytesWritten               : 本次下载的字节数 
  84.  totalBytesWritten          : 已经下载的字节数 
  85.  totalBytesExpectedToWrite  : 下载总大小 
  86.  */  
  87. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite  
  88. {  
  89.     float progress = (float)totalBytesWritten / totalBytesExpectedToWrite;  
  90.       
  91.     [[NSOperationQueue mainQueue] addOperationWithBlock:^{  
  92.         //主线程中更新进度UI操作。。。。  
  93.     }];  
  94. }  
  95.   
  96. /** 续传的代理方法 */  
  97. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes  
  98. {  
  99.     NSLog(@"offset : %lld", fileOffset);  
  100. }  
  101.   
  102. @end  
出处: http://blog.csdn.net/xn4545945
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值