• 文件的上传和下载


    一、文件下载

    获取资源文件大小有两张方式

    1、

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. HTTP HEAD方法  
    2. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeout];  
    3. request.HTTPMethod = @"HEAD";  
    4. [NSURLConnection sendAsynchronousRequest:request queue:self.myQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  
    5.     NSLog(@"%@", response);  
    6.     NSLog(@"---------------");  
    7.     NSLog(@"%@", data);  
    8. }];  
    9. 运行测试代码可以发现,HEAD方法只是返回资源信息,而不会返回数据体  
    10. 应用场景:  
    11. 获取资源Mimetype  
    12. 获取资源文件大小,用于端点续传或多线程下载  

    2

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. 使用块代码获取网络资源大小的方法  
    2. - (void)fileSizeWithURL:(NSURL *)url completion:(void (^)(long long contentLength))completion  
    3. {  
    4.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeout];  
    5.     request.HTTPMethod = @"HEAD";   
    6.     NSURLResponse *response = nil;  
    7.     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];  
    8.       
    9.     completion(response.expectedContentLength);  
    10. }  


    确定每次下载数据包的伪代码实现

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. - (void)downloadFileWithURL:(NSURL *)url  
    2. {  
    3.     [self fileSizeWithURL:url completion:^(long long contentLength) {  
    4.         NSLog(@"文件总大小:%lld", contentLength);          
    5.         // 根据大小下载文件  
    6.                while (contentLength > kDownloadBytes) {  
    7.             NSLog(@"每次下载长度:%lld", (long long)kDownloadBytes);  
    8.             contentLength -= kDownloadBytes;  
    9.         }  
    10.         NSLog(@"最后下载字节数:%lld", contentLength);  
    11.     }];  
    12. }  


    HTTP Range的示例
    通过设置Range可以指定每次从网路下载数据包的大小
    Range示例
    bytes=0-499 从0到499的头500个字节
    bytes=500-999 从500到999的第二个500字节
    bytes=500- 从500字节以后的所有字节
    bytes=-500 最后500个字节
    bytes=500-599,800-899 同时指定几个范围
    Range小结
    - 用于分隔
    前面的数字表示起始字节数
    后面的数组表示截止字节数,没有表示到末尾
    , 用于分组,可以一次指定多个Range,不过很少用

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. 分段Range代码实现  
    2. long long fromBytes = 0;  
    3. long long toBytes = 0;  
    4. while (contentLength > kDownloadBytes) {  
    5.     toBytes = fromBytes + kDownloadBytes - 1;  
    6.     NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", fromBytes, toBytes];  
    7.     NSLog(@"range %@", range);  
    8.     fromBytes += kDownloadBytes;  
    9.     contentLength -= kDownloadBytes;  
    10. }  
    11. fromBytes = fromBytes + contentLength - 1;  
    12. NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", fromBytes, toBytes];  
    13. NSLog(@"range %@", range);  
    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. 分段下载文件  
    2. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeout];  
    3. NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", from, end];  
    4. [request setValue:range forHTTPHeaderField:@"Range"];  
    5.   
    6. NSURLResponse *response = nil;  
    7. NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];  
    8.       
    9. NSLog(@"%@-%@-%ld", range, response, (unsigned long)data.length);  
    10. 提示:  
    11. 如果GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是200(OK)  
    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. 将数据写入文件  
    2. // 打开缓存文件  
    3. NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.cachePath];  
    4. // 如果文件不存在,直接写入数据  
    5. if (!fp) {  
    6.     [data writeToFile:self.cachePath atomically:YES];  
    7. else {  
    8.     // 移动到文件末尾  
    9.     [fp seekToEndOfFile];  
    10.     // 将数据文件追加到文件末尾  
    11.     [fp writeData:data];  
    12.     // 关闭文件句柄  
    13.     [fp closeFile];  
    14. }  
    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. 检查文件大小  
    2. // 判断文件是否存在  
    3. if ([[NSFileManager defaultManager] fileExistsAtPath:self.cachePath]) {  
    4.     NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cachePath error:NULL];  
    5.     return [dict[NSFileSize] longLongValue];  
    6. else {  
    7.     return 0;  
    8. }  
    9.   
    10. 提示:由于数据是追加的,为了避免重复从网络下载文件,在下载之前  
    11. 判断缓存路径中文件是否已经存在  
    12. 如果存在检查文件大小  
    13. 如果文件大小与网络资源大小一致,则不再下载  


    全部代码如下

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. //  
    2. //  MJViewController.m  
    3. //  01.文件下载  
    4. //  
    5. //  Created by apple on 14-4-29.  
    6. //  Copyright (c) 2014年 itcast. All rights reserved.  
    7. //  
    8.   
    9. #import "MJViewController.h"  
    10. #import "FileDownload.h"  
    11.   
    12. @interface MJViewController ()  
    13. @property (nonatomic, strong) FileDownload *download;  
    14. @property (weak, nonatomic) IBOutlet UIImageView *imageView;  
    15. @end  
    16.   
    17. @implementation MJViewController  
    18.   
    19. - (void)viewDidLoad  
    20. {  
    21.     [super viewDidLoad];  
    22.       
    23.     self.download = [[FileDownload alloc] init];  
    24.     [self.download downloadFileWithURL:[NSURL URLWithString:@"http://localhost/itcast/images/head4.png"] completion:^(UIImage *image) {  
    25.           
    26.         self.imageView.image = image;  
    27.     }];  
    28. }  
    29.   
    30. @end  
    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. //  
    2. //  FileDownload.m  
    3. //  01.文件下载  
    4. //  
    5. //  Created by apple on 14-4-29.  
    6. //  Copyright (c) 2014年 itcast. All rights reserved.  
    7. //  
    8.   
    9. #import "FileDownload.h"  
    10. #import "NSString+Password.h"  
    11.   
    12. #define kTimeOut        2.0f  
    13. // 每次下载的字节数  
    14. #define kBytesPerTimes  20250  
    15.   
    16. @interface FileDownload()  
    17. @property (nonatomic, strong) NSString *cacheFile;  
    18. @property (nonatomic, strong) UIImage *cacheImage;  
    19. @end  
    20.   
    21. @implementation FileDownload  
    22. /** 
    23.  为了保证开发的简单,所有方法都不使用多线程,所有的注意力都保持在文件下载上 
    24.   
    25.  在开发中如果碰到比较绕的计算问题时,建议: 
    26.  1> 测试数据不要太大 
    27.  2> 测试数据的数值变化,能够用笔算计算出准确的数值 
    28.  3> 编写代码对照测试 
    29.  
    30.  */  
    31. //- (NSString *)cacheFile  
    32. //{  
    33. //    if (!_cacheFile) {  
    34. //        NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];  
    35. //        _cacheFile = [cacheDir stringByAppendingPathComponent:@"123.png"];  
    36. //    }  
    37. //    return _cacheFile;  
    38. //}  
    39. - (UIImage *)cacheImage  
    40. {  
    41.     if (!_cacheImage) {  
    42.         _cacheImage = [UIImage imageWithContentsOfFile:self.cacheFile];  
    43.     }  
    44.     return _cacheImage;  
    45. }  
    46.   
    47. - (void)setCacheFile:(NSString *)urlStr  
    48. {  
    49.     NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];  
    50.     urlStr = [urlStr MD5];  
    51.       
    52.     _cacheFile = [cacheDir stringByAppendingPathComponent:urlStr];  
    53. }  
    54.   
    55. - (void)downloadFileWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion  
    56. {  
    57.     // GCD中的串行队列异步方法  
    58.     dispatch_queue_t q = dispatch_queue_create("cn.itcast.download", DISPATCH_QUEUE_SERIAL);  
    59.       
    60.     dispatch_async(q, ^{  
    61.         NSLog(@"%@", [NSThread currentThread]);  
    62.           
    63.         // 把对URL进行MD5加密之后的结果当成文件名  
    64.         self.cacheFile = [url absoluteString];  
    65.           
    66.         // 1. 从网络下载文件,需要知道这个文件的大小  
    67.         long long fileSize = [self fileSizeWithURL:url];  
    68.         // 计算本地缓存文件大小  
    69.         long long cacheFileSize = [self localFileSize];  
    70.           
    71.         if (cacheFileSize == fileSize) {  
    72.             dispatch_async(dispatch_get_main_queue(), ^{  
    73.                 completion(self.cacheImage);  
    74.             });  
    75.             NSLog(@"文件已经存在");  
    76.             return;  
    77.         }  
    78.           
    79.         // 2. 确定每个数据包的大小  
    80.         long long fromB = 0;  
    81.         long long toB = 0;  
    82.         // 计算起始和结束的字节数  
    83.         while (fileSize > kBytesPerTimes) {  
    84.             // 20480 + 20480  
    85.             //  
    86.             toB = fromB + kBytesPerTimes - 1;  
    87.               
    88.             // 3. 分段下载文件  
    89.             [self downloadDataWithURL:url fromB:fromB toB:toB];  
    90.               
    91.             fileSize -= kBytesPerTimes;  
    92.             fromB += kBytesPerTimes;  
    93.         }  
    94.         [self downloadDataWithURL:url fromB:fromB toB:fromB + fileSize - 1];  
    95.   
    96.         dispatch_async(dispatch_get_main_queue(), ^{  
    97.             completion(self.cacheImage);  
    98.         });          
    99.     });  
    100. }  
    101.   
    102. #pragma mark 下载指定字节范围的数据包  
    103. /** 
    104.  NSURLRequestUseProtocolCachePolicy = 0,        // 默认的缓存策略,内存缓存 
    105.   
    106.  NSURLRequestReloadIgnoringLocalCacheData = 1,  // 忽略本地的内存缓存 
    107.  NSURLRequestReloadIgnoringCacheData 
    108.  */  
    109. - (void)downloadDataWithURL:(NSURL *)url fromB:(long long)fromB toB:(long long)toB  
    110. {  
    111.     NSLog(@"数据包:%@", [NSThread currentThread]);  
    112.       
    113.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeOut];  
    114.       
    115.     // 指定请求中所要GET的字节范围  
    116.     NSString *range = [NSString stringWithFormat:@"Bytes=%lld-%lld", fromB, toB];  
    117.     [request setValue:range forHTTPHeaderField:@"Range"];  
    118.     NSLog(@"%@", range);  
    119.       
    120.     NSURLResponse *response = nil;  
    121.     NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];  
    122.       
    123.     // 写入文件,覆盖文件不会追加  
    124. //    [data writeToFile:@"/Users/aplle/Desktop/1.png" atomically:YES];  
    125.     [self appendData:data];  
    126.       
    127.     NSLog(@"%@", response);  
    128. }  
    129.   
    130. #pragma mark - 读取本地缓存文件大小  
    131. - (long long)localFileSize  
    132. {  
    133.     // 读取本地文件信息  
    134.     NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cacheFile error:NULL];  
    135.     NSLog(@"%lld", [dict[NSFileSize] longLongValue]);  
    136.       
    137.     return [dict[NSFileSize] longLongValue];  
    138. }  
    139.   
    140. #pragma mark - 追加数据到文件  
    141. - (void)appendData:(NSData *)data  
    142. {  
    143.     // 判断文件是否存在  
    144.     NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.cacheFile];  
    145.     // 如果文件不存在创建文件  
    146.     if (!fp) {  
    147.         [data writeToFile:self.cacheFile atomically:YES];  
    148.     } else {  
    149.         // 如果文件已经存在追加文件  
    150.         // 1> 移动到文件末尾  
    151.         [fp seekToEndOfFile];  
    152.         // 2> 追加数据  
    153.         [fp writeData:data];  
    154.         // 3> 写入文件  
    155.         [fp closeFile];  
    156.     }  
    157. }  
    158.   
    159. #pragma mark - 获取网络文件大小  
    160. - (long long)fileSizeWithURL:(NSURL *)url  
    161. {  
    162.     // 默认是GET  
    163.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeOut];  
    164.       
    165.     // HEAD 头,只是返回文件资源的信息,不返回具体是数据  
    166.     // 如果要获取资源的MIMEType,也必须用HEAD,否则,数据会被重复下载两次  
    167.     request.HTTPMethod = @"HEAD";  
    168.   
    169.     // 使用同步方法获取文件大小  
    170.     NSURLResponse *response = nil;  
    171.       
    172.     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];  
    173.       
    174.     // expectedContentLength文件在网络上的大小  
    175.     NSLog(@"%lld", response.expectedContentLength);  
    176.       
    177.     return response.expectedContentLength;  
    178. }  
    179.   
    180. @end  

    二、文件上传

    代码如下

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. //  
    2. //  MJViewController.m  
    3. //  02.Post上传  
    4. //  
    5. //  Created by apple on 14-4-29.  
    6. //  Copyright (c) 2014年 itcast. All rights reserved.  
    7. //  
    8.   
    9. #import "MJViewController.h"  
    10. #import "UploadFile.h"  
    11.   
    12. @interface MJViewController ()  
    13.   
    14. @end  
    15.   
    16. @implementation MJViewController  
    17.   
    18. - (void)viewDidLoad  
    19. {  
    20.     [super viewDidLoad];  
    21.   
    22.     UploadFile *upload = [[UploadFile alloc] init];  
    23.       
    24.     NSString *urlString = @"http://localhost/upload.php";  
    25.       
    26.     NSString *path = [[NSBundle mainBundle] pathForResource:@"头像1.png" ofType:nil];  
    27.     NSData *data = [NSData dataWithContentsOfFile:path];  
    28.       
    29.     [upload uploadFileWithURL:[NSURL URLWithString:urlString] data:data];  
    30. }  
    31.   
    32. @end  
    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
      1. //  
      2. //  UploadFile.m  
      3. //  02.Post上传  
      4. //  
      5. //  Created by apple on 14-4-29.  
      6. //  Copyright (c) 2014年 itcast. All rights reserved.  
      7. //  
      8.   
      9. #import "UploadFile.h"  
      10.   
      11. @implementation UploadFile  
      12. // 拼接字符串  
      13. static NSString *boundaryStr = @"--";   // 分隔字符串  
      14. static NSString *randomIDStr;           // 本次上传标示字符串  
      15. static NSString *uploadID;              // 上传(php)脚本中,接收文件字段  
      16.   
      17. - (instancetype)init  
      18. {  
      19.     self = [super init];  
      20.     if (self) {  
      21.         randomIDStr = @"itcast";  
      22.         uploadID = @"uploadFile";  
      23.     }  
      24.     return self;  
      25. }  
      26.   
      27. #pragma mark - 私有方法  
      28. - (NSString *)topStringWithMimeType:(NSString *)mimeType uploadFile:(NSString *)uploadFile  
      29. {  
      30.     NSMutableString *strM = [NSMutableString string];  
      31.       
      32.     [strM appendFormat:@"%@%@ ", boundaryStr, randomIDStr];  
      33.     [strM appendFormat:@"Content-Disposition: form-data; name="%@"; filename="%@" ", uploadID, uploadFile];  
      34.     [strM appendFormat:@"Content-Type: %@ ", mimeType];  
      35.       
      36.     NSLog(@"%@", strM);  
      37.     return [strM copy];  
      38. }  
      39.   
      40. - (NSString *)bottomString  
      41. {  
      42.     NSMutableString *strM = [NSMutableString string];  
      43.       
      44.     [strM appendFormat:@"%@%@ ", boundaryStr, randomIDStr];  
      45.     [strM appendString:@"Content-Disposition: form-data; name="submit" "];  
      46.     [strM appendString:@"Submit "];  
      47.     [strM appendFormat:@"%@%@-- ", boundaryStr, randomIDStr];  
      48.       
      49.     NSLog(@"%@", strM);  
      50.     return [strM copy];  
      51. }  
      52.   
      53. #pragma mark - 上传文件  
      54. - (void)uploadFileWithURL:(NSURL *)url data:(NSData *)data  
      55. {  
      56.     // 1> 数据体  
      57.     NSString *topStr = [self topStringWithMimeType:@"image/png" uploadFile:@"头像1.png"];  
      58.     NSString *bottomStr = [self bottomString];  
      59.       
      60.     NSMutableData *dataM = [NSMutableData data];  
      61.     [dataM appendData:[topStr dataUsingEncoding:NSUTF8StringEncoding]];  
      62.     [dataM appendData:data];  
      63.     [dataM appendData:[bottomStr dataUsingEncoding:NSUTF8StringEncoding]];  
      64.       
      65.     // 1. Request  
      66.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f];  
      67.       
      68.     // dataM出了作用域就会被释放,因此不用copy  
      69.     request.HTTPBody = dataM;  
      70.       
      71.     // 2> 设置Request的头属性  
      72.     request.HTTPMethod = @"POST";  
      73.       
      74.     // 3> 设置Content-Length  
      75.     NSString *strLength = [NSString stringWithFormat:@"%ld", (long)dataM.length];  
      76.     [request setValue:strLength forHTTPHeaderField:@"Content-Length"];  
      77.       
      78.     // 4> 设置Content-Type  
      79.     NSString *strContentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", randomIDStr];  
      80.     [request setValue:strContentType forHTTPHeaderField:@"Content-Type"];  
      81.       
      82.     // 3> 连接服务器发送请求  
      83.     [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  
      84.           
      85.         NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
      86.         NSLog(@"%@", result);  
      87.     }];  
      88. }  
      89.   
      90.   
      91.   
      92. @end  
  • 相关阅读:
    简易表格练习
    CSS圆角样式
    力不从心
    学渣在努力~
    嫌疑人
    poj1308 Is it a tree?
    悲剧文本
    迷宫问题
    n皇后问题
    油田
  • 原文地址:https://www.cnblogs.com/wmwblog/p/5371266.html
Copyright © 2020-2023  润新知