標籤:des style blog http 使用 os
一、檔案下載
擷取資源檔大小有兩張方式
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); }];}
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,不過很少用
分段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