獲取資源文件大小有兩張方式
1、
HTTP HEAD方法 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeout]; request.HTTPMethod = @"HEAD"; [NSURLConnection sendAsynchronousRequest:request queue:self.myQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSLog(@"%@", response); NSLog(@"---------------"); NSLog(@"%@", data); }]; 運行測試代碼可以發現,HEAD方法只是返回資源信息,而不會返回數據體 應用場景: 獲取資源Mimetype 獲取資源文件大小,用於端點續傳或多線程下載2
使用塊代碼獲取網絡資源大小的方法 - (void)fileSizeWithURL:(NSURL *)url completion:(void (^)(long long contentLength))completion { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeout]; request.HTTPMethod = @"HEAD"; NSURLResponse *response = nil; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; completion(response.expectedContentLength); }
- (void)downloadFileWithURL:(NSURL *)url { [self fileSizeWithURL:url completion:^(long long contentLength) { NSLog(@"文件總大小:%lld", contentLength); // 根據大小下載文件 while (contentLength > kDownloadBytes) { NSLog(@"每次下載長度:%lld", (long long)kDownloadBytes); contentLength -= kDownloadBytes; } NSLog(@"最後下載字節數:%lld", contentLength); }]; }
分段Range代碼實現 long long fromBytes = 0; long long toBytes = 0; while (contentLength > kDownloadBytes) { toBytes = fromBytes + kDownloadBytes - 1; NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", fromBytes, toBytes]; NSLog(@"range %@", range); fromBytes += kDownloadBytes; contentLength -= kDownloadBytes; } fromBytes = fromBytes + contentLength - 1; NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", fromBytes, toBytes]; NSLog(@"range %@", range);
分段下載文件 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeout]; NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld", from, end]; [request setValue:range forHTTPHeaderField:@"Range"]; NSURLResponse *response = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; NSLog(@"%@-%@-%ld", range, response, (unsigned long)data.length); 提示: 如果GET包含Range請求頭,響應會以狀態碼206(PartialContent)返回而不是200(OK)
將數據寫入文件 // 打開緩存文件 NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.cachePath]; // 如果文件不存在,直接寫入數據 if (!fp) { [data writeToFile:self.cachePath atomically:YES]; } else { // 移動到文件末尾 [fp seekToEndOfFile]; // 將數據文件追加到文件末尾 [fp writeData:data]; // 關閉文件句柄 [fp closeFile]; }
檢查文件大小 // 判斷文件是否存在 if ([[NSFileManager defaultManager] fileExistsAtPath:self.cachePath]) { NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cachePath error:NULL]; return [dict[NSFileSize] longLongValue]; } else { return 0; } 提示:由於數據是追加的,為了避免重復從網絡下載文件,在下載之前 判斷緩存路徑中文件是否已經存在 如果存在檢查文件大小 如果文件大小與網絡資源大小一致,則不再下載
// // MJViewController.m // 01.文件下載 // // Created by apple on 14-4-29. // Copyright (c) 2014年 itcast. All rights reserved. // #import "MJViewController.h" #import "FileDownload.h" @interface MJViewController () @property (nonatomic, strong) FileDownload *download; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation MJViewController - (void)viewDidLoad { [super viewDidLoad]; self.download = [[FileDownload alloc] init]; [self.download downloadFileWithURL:[NSURL URLWithString:@"http://localhost/itcast/images/head4.png"] completion:^(UIImage *image) { self.imageView.image = image; }]; } @end
// // FileDownload.m // 01.文件下載 // // Created by apple on 14-4-29. // Copyright (c) 2014年 itcast. All rights reserved. // #import "FileDownload.h" #import "NSString+Password.h" #define kTimeOut 2.0f // 每次下載的字節數 #define kBytesPerTimes 20250 @interface FileDownload() @property (nonatomic, strong) NSString *cacheFile; @property (nonatomic, strong) UIImage *cacheImage; @end @implementation FileDownload /** 為了保證開發的簡單,所有方法都不使用多線程,所有的注意力都保持在文件下載上 在開發中如果碰到比較繞的計算問題時,建議: 1> 測試數據不要太大 2> 測試數據的數值變化,能夠用筆算計算出准確的數值 3> 編寫代碼對照測試 */ //- (NSString *)cacheFile //{ // if (!_cacheFile) { // NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // _cacheFile = [cacheDir stringByAppendingPathComponent:@"123.png"]; // } // return _cacheFile; //} - (UIImage *)cacheImage { if (!_cacheImage) { _cacheImage = [UIImage imageWithContentsOfFile:self.cacheFile]; } return _cacheImage; } - (void)setCacheFile:(NSString *)urlStr { NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; urlStr = [urlStr MD5]; _cacheFile = [cacheDir stringByAppendingPathComponent:urlStr]; } - (void)downloadFileWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion { // GCD中的串行隊列異步方法 dispatch_queue_t q = dispatch_queue_create("cn.itcast.download", DISPATCH_QUEUE_SERIAL); dispatch_async(q, ^{ NSLog(@"%@", [NSThread currentThread]); // 把對URL進行MD5加密之後的結果當成文件名 self.cacheFile = [url absoluteString]; // 1. 從網絡下載文件,需要知道這個文件的大小 long long fileSize = [self fileSizeWithURL:url]; // 計算本地緩存文件大小 long long cacheFileSize = [self localFileSize]; if (cacheFileSize == fileSize) { dispatch_async(dispatch_get_main_queue(), ^{ completion(self.cacheImage); }); NSLog(@"文件已經存在"); return; } // 2. 確定每個數據包的大小 long long fromB = 0; long long toB = 0; // 計算起始和結束的字節數 while (fileSize > kBytesPerTimes) { // 20480 + 20480 // toB = fromB + kBytesPerTimes - 1; // 3. 分段下載文件 [self downloadDataWithURL:url fromB:fromB toB:toB]; fileSize -= kBytesPerTimes; fromB += kBytesPerTimes; } [self downloadDataWithURL:url fromB:fromB toB:fromB + fileSize - 1]; dispatch_async(dispatch_get_main_queue(), ^{ completion(self.cacheImage); }); }); } #pragma mark 下載指定字節范圍的數據包 /** NSURLRequestUseProtocolCachePolicy = 0, // 默認的緩存策略,內存緩存 NSURLRequestReloadIgnoringLocalCacheData = 1, // 忽略本地的內存緩存 NSURLRequestReloadIgnoringCacheData */ - (void)downloadDataWithURL:(NSURL *)url fromB:(long long)fromB toB:(long long)toB { NSLog(@"數據包:%@", [NSThread currentThread]); NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeOut]; // 指定請求中所要GET的字節范圍 NSString *range = [NSString stringWithFormat:@"Bytes=%lld-%lld", fromB, toB]; [request setValue:range forHTTPHeaderField:@"Range"]; NSLog(@"%@", range); NSURLResponse *response = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; // 寫入文件,覆蓋文件不會追加 // [data writeToFile:@"/Users/aplle/Desktop/1.png" atomically:YES]; [self appendData:data]; NSLog(@"%@", response); } #pragma mark - 讀取本地緩存文件大小 - (long long)localFileSize { // 讀取本地文件信息 NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cacheFile error:NULL]; NSLog(@"%lld", [dict[NSFileSize] longLongValue]); return [dict[NSFileSize] longLongValue]; } #pragma mark - 追加數據到文件 - (void)appendData:(NSData *)data { // 判斷文件是否存在 NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.cacheFile]; // 如果文件不存在創建文件 if (!fp) { [data writeToFile:self.cacheFile atomically:YES]; } else { // 如果文件已經存在追加文件 // 1> 移動到文件末尾 [fp seekToEndOfFile]; // 2> 追加數據 [fp writeData:data]; // 3> 寫入文件 [fp closeFile]; } } #pragma mark - 獲取網絡文件大小 - (long long)fileSizeWithURL:(NSURL *)url { // 默認是GET NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeOut]; // HEAD 頭,只是返回文件資源的信息,不返回具體是數據 // 如果要獲取資源的MIMEType,也必須用HEAD,否則,數據會被重復下載兩次 request.HTTPMethod = @"HEAD"; // 使用同步方法獲取文件大小 NSURLResponse *response = nil; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; // expectedContentLength文件在網絡上的大小 NSLog(@"%lld", response.expectedContentLength); return response.expectedContentLength; } @end
代碼如下
// // MJViewController.m // 02.Post上傳 // // Created by apple on 14-4-29. // Copyright (c) 2014年 itcast. All rights reserved. // #import "MJViewController.h" #import "UploadFile.h" @interface MJViewController () @end @implementation MJViewController - (void)viewDidLoad { [super viewDidLoad]; UploadFile *upload = [[UploadFile alloc] init]; NSString *urlString = @"http://localhost/upload.php"; NSString *path = [[NSBundle mainBundle] pathForResource:@"頭像1.png" ofType:nil]; NSData *data = [NSData dataWithContentsOfFile:path]; [upload uploadFileWithURL:[NSURL URLWithString:urlString] data:data]; } @end
// // UploadFile.m // 02.Post上傳 // // Created by apple on 14-4-29. // Copyright (c) 2014年 itcast. All rights reserved. // #import "UploadFile.h" @implementation UploadFile // 拼接字符串 static NSString *boundaryStr = @"--"; // 分隔字符串 static NSString *randomIDStr; // 本次上傳標示字符串 static NSString *uploadID; // 上傳(php)腳本中,接收文件字段 - (instancetype)init { self = [super init]; if (self) { randomIDStr = @"itcast"; uploadID = @"uploadFile"; } return self; } #pragma mark - 私有方法 - (NSString *)topStringWithMimeType:(NSString *)mimeType uploadFile:(NSString *)uploadFile { NSMutableString *strM = [NSMutableString string]; [strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr]; [strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\n", uploadID, uploadFile]; [strM appendFormat:@"Content-Type: %@\n\n", mimeType]; NSLog(@"%@", strM); return [strM copy]; } - (NSString *)bottomString { NSMutableString *strM = [NSMutableString string]; [strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr]; [strM appendString:@"Content-Disposition: form-data; name=\"submit\"\n\n"]; [strM appendString:@"Submit\n"]; [strM appendFormat:@"%@%@--\n", boundaryStr, randomIDStr]; NSLog(@"%@", strM); return [strM copy]; } #pragma mark - 上傳文件 - (void)uploadFileWithURL:(NSURL *)url data:(NSData *)data { // 1> 數據體 NSString *topStr = [self topStringWithMimeType:@"image/png" uploadFile:@"頭像1.png"]; NSString *bottomStr = [self bottomString]; NSMutableData *dataM = [NSMutableData data]; [dataM appendData:[topStr dataUsingEncoding:NSUTF8StringEncoding]]; [dataM appendData:data]; [dataM appendData:[bottomStr dataUsingEncoding:NSUTF8StringEncoding]]; // 1. Request NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f]; // dataM出了作用域就會被釋放,因此不用copy request.HTTPBody = dataM; // 2> 設置Request的頭屬性 request.HTTPMethod = @"POST"; // 3> 設置Content-Length NSString *strLength = [NSString stringWithFormat:@"%ld", (long)dataM.length]; [request setValue:strLength forHTTPHeaderField:@"Content-Length"]; // 4> 設置Content-Type NSString *strContentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", randomIDStr]; [request setValue:strContentType forHTTPHeaderField:@"Content-Type"]; // 3> 連接服務器發送請求 [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@", result); }]; } @end