小文件下載
如果文件比較小,下載方式會比較多
直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
利用NSURLConnection發送一個HTTP請求(默認是get請求)去下載
如果是下載圖片,還可以利用SDWebImage框架
如果是大文件下載,建議使用或者使用NSURLSession第三方框架
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,不過很少用
第三方解壓縮框架——SSZipArchive
下載地址:https://github.com/samsoffes/ssziparchive
注意:需要引入libz.dylib框架
Unzipping
NSString *zipPath = @"path_to_your_zip_file";
NSString *destinationPath = @"path_to_the_folder_where_you_want_it_unzipped";
[SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
Zipping
NSString *zippedPath = @"path_where_you_want_the_file_created";
NSArray *inputPaths = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource:@"photo1" ofType:@"jpg"],
[[NSBundle mainBundle] pathForResource:@"photo2" ofType:@"jpg"]
nil];
[SSZipArchive createZipFileAtPath:zippedPath withFilesAtPaths:inputPaths];
第三方解壓縮框架——ZipArchive
下載地址:https://github.com/ZipArchive/ZipArchive
需要引入libz.dylib框架
導入頭文件Main.h
創建壓縮文件
- (BOOL)createZipFileAtPath:(NSString *)path
withFilesAtPaths:(NSArray *)paths; - (BOOL)createZipFileAtPath:(NSString *)path
withContentsOfDirectory:(NSString *)directoryPath;
解壓
- (BOOL)unzipFileAtPath:(NSString *)path
toDestination:(NSString *)destination
文件上傳的步驟
設置請求頭
[request setValue:@"multipart/form-data; boundary=分割線" forHTTPHeaderField:@"Content-Type"];
設置請求體
非文件參數
--分割線\r\n
Content-Disposition: form-data; name="參數名"\r\n
\r\n
參數值
\r\n
文件參數
--分割線\r\n
Content-Disposition: form-data; name="參數名"; filename="文件名"\r\n
Content-Type: 文件的MIMEType\r\n
\r\n
文件數據
\r\n
參數結束的標記
--分割線--\r\n
multipart/form-data格式小結
部分文件的MIMEType
獲得文件的MIMEType
利用NSURLConnection
(NSString *)MIMEType:(NSURL *)url
{
1.創建一個請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
2.發送請求(返回響應)
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:nil];
3.獲得MIMEType
return response.MIMEType;
}
獲得文件的MIMEType
C語言API
- (NSString *)mimeTypeForFileAtPath:(NSString *)path
{
if (![[NSFileManager alloc] init]
fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI =UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
(CFStringRef)[path pathExtension], NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI,
kUTTagClassMIMEType);
CFRelease(UTI);
if (!MIMEType) {
return
@"application/octet-stream";
}
return NSMakeCollectable(MIMEType);
}
1.0 文件下載
- 1.1 小文件下載
(1)第一種方式(NSData)
使用NSDta直接加載網絡上的url資源(不考慮線程)
-(void)dataDownload
{
1.確定資源路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
2.根據URL加載對應的資源
NSData *data = [NSData dataWithContentsOfURL:url];
3.轉換并顯示數據
UIImage *image = [UIImage imageWithData:data];
self.imageView.image = image;
}
(2)第二種方式(NSURLConnection-sendAsync)
使用NSURLConnection發送異步請求下載文件資源
-(void)connectDownload
{
1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
2.創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
3.使用NSURLConnection發送一個異步請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue
mainQueue] completionHandler:^(NSURLResponse * _Nullable
response, NSData * _Nullable data, NSError *
_Nullable connectionError) {
4.拿到并處理數據
UIImage *image = [UIImage imageWithData:data];
self.imageView.image = image;
}];
}
(3)第三種方式(NSURLConnection-delegate)
使用NSURLConnection設置代理發送異步請求的方式下載文件
-(void)connectionDelegateDownload
{
1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
2.創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
3.使用NSURLConnection設置代理并發送異步請求
[NSURLConnection connectionWithRequest:request delegate:self];
}
pragma
mark--NSURLConnectionDataDelegate
當接收到服務器響應的時候調用,該方法只會調用一次
-(void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
創建一個容器,用來接收服務器返回的數據
self.fileData = [NSMutableData data];
獲得當前要下載文件的總大?。ㄍㄟ^響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
self.totalLength = res.expectedContentLength;
NSLog(@"%zd",self.totalLength);
拿到服務器端推薦的文件名稱
self.fileName = res.suggestedFilename;
}
當接收到服務器返回的數據時會調用
該方法可能會被調用多次
-(void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
拼接每次下載的數據
[self.fileData appendData:data];
計算當前下載進度并刷新UI顯示
self.currentLength = self.fileData.length;
NSLog(@"%f",1.0* self.currentLength/self.totalLength);
self.progressView.progress = 1.0*self.currentLength/self.totalLength;
}
當網絡請求結束之后調用
-(void)connectionDidFinishLoading:(NSURLConnection
*)connection
{
文件下載完畢把接受到的文件數據寫入到沙盒中保存
1.確定要保存文件的全路徑
caches文件夾路徑
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES) lastObject];
NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];
2.寫數據到文件中
[self.fileData writeToFile:fullPath atomically:YES];
NSLog(@"%@",fullPath);
}
當請求失敗的時候調用該方法
-(void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
NSLog(@"%s",__func__);
}
- 3.2 大文件的下載
(1)實現思路
邊接收數據邊寫文件以解決內存越來越大的問題
(2)核心代碼
當接收到服務器響應的時候調用,該方法只會調用一次
-(void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
0.獲得當前要下載文件的總大?。ㄍㄟ^響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
self.totalLength = res.expectedContentLength;
NSLog(@"%zd",self.totalLength);
創建一個新的文件,用來當接收到服務器返回數據的時候往該文件中寫入數據
1.獲取文件管理者
NSFileManager *manager = [NSFileManager defaultManager];
2.拼接文件的全路徑
caches文件夾路徑
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES) lastObject];
NSString *fullPath = [caches stringByAppendingPathComponent:res.suggestedFilename];
self.fullPath = fullPath;
3.創建一個空的文件
[manager createFileAtPath:fullPath contents:nil
attributes:nil];
}
當接收到服務器返回的數據時會調用
該方法可能會被調用多次
-(void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
1.創建一個用來向文件中寫數據的文件句柄
注意當下載完成之后,該文件句柄需要關閉,調用closeFile方法
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
2.設置寫數據的位置(追加)
[handle
seekToEndOfFile];
3.寫數據
[handle writeData:data];
4.計算當前文件的下載進度
self.currentLength += data.length;
NSLog(@"%f",1.0* self.currentLength/self.totalLength);
self.progressView.progress = 1.0*self.currentLength/self.totalLength;
}
- 1.3 大文件斷點下載
(1)實現思路
在下載文件的時候不再是整塊的從頭開始下載,而是看當前文件已經下載到哪個地方,然后從該地方接著往后面下載??梢酝ㄟ^在請求對象中設置請求頭實現。
(2)解決方案(設置請求頭)
2.創建請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
2.1 設置下載文件的某一部分
只要設置HTTP請求頭的Range屬性, 就可以實現從指定位置開始下載
表示頭500個字節:Range: bytes=0-499
表示第二個500字節:Range: bytes=500-999
表示最后500個字節:Range: bytes=-500
表示500字節以后的范圍:Range: bytes=500-
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
(3)注意點(下載進度并判斷是否需要重新創建文件)
獲得當前要下載文件的總大小(通過響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
注意點:res.expectedContentLength獲得是本次請求要下載的文件的大小(并非是完整的文件的大?。?/p>
因此:文件的總大小 == 本次要下載的文件大小+已經下載的文件的大小
self.totalLength = res.expectedContentLength +self.currentLength;
NSLog(@"----------------------------%zd",self.totalLength);
0 判斷當前是否已經下載過,如果當前文件已經存在,那么直接返回
if (self.currentLength >0) {
return;
}
- 1.4 輸出流
(1)使用輸出流也可以實現和NSFileHandle相同的功能
(2)如何使用
1.創建一個數據輸出流
第一個參數:二進制的流數據要寫入到哪里
第二個參數:采用什么樣的方式寫入流數據,如果YES則表示追加,如果是NO則表示覆蓋
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];
只要調用了該方法就會往文件中寫數據
如果文件不存在,那么會自動的創建一個
[stream open];
self.stream = stream;
2.當接收到數據的時候寫數據
使用輸出流寫數據
第一個參數:要寫入的二進制數據
第二個參數:要寫入的數據的大小
[self.stream write:data.bytes maxLength:data.length];
3.當文件下載完畢的時候關閉輸出流
關閉輸出流
[self.stream close];
self.stream = nil;
- 1.5 使用多線程下載文件思路
01 開啟多條線程,每條線程都只下載文件的一部分(通過設置請求頭中的Range來實現)
02 創建一個和需要下載文件大小一致的文件,判斷當前是那個線程,根據當前的線程來判斷下載的數據應該寫入到文件中的哪個位置。(假設開5條線程來下載10M的文件,那么線程1下載0-2M,線程2下載2-4M一次類推,當接收到服務器返回的數據之后應該先判斷當前線程是哪個線程,假如當前線程是線程2,那么在寫數據的時候就從文件的2M位置開始寫入)
03 代碼相關:使用NSFileHandle這個類的seekToFileOfSet方法,來向文件中特定的位置寫入數據。
04 技術相關
a.每個線程通過設置請求頭下載文件中的某一個部分
b.通過NSFileHandle向文件中的指定位置寫數據
2.0 文件的壓縮和解壓縮
(1)說明
使用ZipArchive來壓縮和解壓縮文件需要添加依賴庫(libz),使用需要包含Main文件,如果使用cocoaPoads來安裝框架,那么會自動的配置框架的使用環境
(2)相關代碼
壓縮文件的第一種方式
第一個參數:壓縮文件要保存的位置
第二個參數:要壓縮哪幾個文件
[Main createZipFileAtPath:fullpath withFilesAtPaths:arrayM];
壓縮文件的第二種方式
第一個參數:文件壓縮到哪個地方
第二個參數:要壓縮文件的全路徑
[Main createZipFileAtPath:fullpath withContentsOfDirectory:zipFile];
如何對壓縮文件進行解壓
第一個參數:要解壓的文件
第二個參數:要解壓到什么地方
[Main unzipFileAtPath:unZipFile toDestination:fullpath];
2.0 文件的上傳
- 2.1 文件上傳步驟
(1)確定請求路徑
(2)根據URL創建一個可變的請求對象
(3)設置請求對象,修改請求方式為POST
(4)設置請求頭,告訴服務器我們將要上傳文件(Content-Type)
(5)設置請求體(在請求體中按照既定的格式拼接要上傳的文件參數和非文件參數等數據)
001 拼接文件參數
002 拼接非文件參數
003 添加結尾標記
(6)使用NSURLConnection sendAsync發送異步請求上傳文件
(7)解析服務器返回的數據
- 2.2 文件上傳設置請求體的數據格式
請求體拼接格式
分隔符:----WebKitFormBoundaryhBDKBUWBHnAgvz9c
01.文件參數拼接格式
--分隔符
Content-Disposition:參數
Content-Type:參數
空行
文件參數
02.非文件拼接參數
--分隔符
Content-Disposition:參數
空行
非文件的二進制數據
03.結尾標識
--分隔符--
- 2.3 文件上傳相關代碼
- (void)upload
{
1.確定請求路徑
NSURL
*url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
2.創建一個可變的請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
3.設置請求方式為POST
request.HTTPMethod = @"POST";
4.設置請求頭
NSString *filed = [NSString stringWithFormat:@"multipart/form-data;
boundary=%@",Kboundary];
[request setValue:filed forHTTPHeaderField:@"Content-Type"];
5.設置請求體
NSMutableData *data = [NSMutableData data];
5.1
文件參數
--分隔符
Content-Disposition:參數
Content-Type:參數
空行
文件參數
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Disposition: form-data;
name="file"; filename="test.png""dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Type: image/png"
dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
[data appendData:KnewLine];
UIImage *image = [UIImage imageNamed:@"test"];
NSData *imageData = UIImagePNGRepresentation(image);
[data appendData:imageData];
[data appendData:KnewLine];
5.2
非文件參數
--分隔符
Content-Disposition:參數
空行
非文件參數的二進制數據
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Disposition: form-data;
name="username"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
[data appendData:KnewLine];
NSData *nameData = [@"wending ding" dataUsingEncoding:NSUTF8StringEncoding];
[data appendData:nameData];
[data appendData:KnewLine];
5.3
結尾標識
--分隔符--
[data appendData:[[NSString stringWithFormat:@"--%@--",Kboundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
request.HTTPBody = data;
6.發送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError
-
__nullable connectionError) {
7.解析服務器返回的數據
NSLog(@"%@",[NSJSONSerialization
JSONObjectWithData:data options:kNilOptions error:nil]);}];
}
- 2.4 如何獲得文件的MIMEType類型
(1)直接對該對象發送一個異步網絡請求,在響應頭中通過response.MIMEType拿到文件的MIMEType類型
如果想要及時拿到該數據,那么可以發送一個同步請求
- (NSString *)getMIMEType
{
NSString *filePath = @"/Users/文頂頂/Desktop/備課/其它/swift.md";
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL
fileURLWithPath:filePath]] returningResponse:&response error:nil];
return response.MIMEType;
}
對該文件發送一個異步請求,拿到文件的MIMEType
- (void)MIMEType
{
NSString *file = @"file:///Users/Edison/Desktop/test.png";
[NSURLConnection sendAsynchronousRequest:[NSURLRequest
requestWithURL:[NSURL fileURLWithPath:@"/Users/Edison/Desktop/test.png"]] queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data,
NSError * __nullableconnectionError) {
NSLog(@"%@",response.MIMEType);
}];
}
(2)通過UTTypeCopyPreferredTagWithClass方法
注意:需要依賴于框架MobileCoreServices
- (NSString *)mimeTypeForFileAtPath:(NSString *)path
{
if (![[[NSFileManager alloc] init]
fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension],NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI,
kUTTagClassMIMEType);
CFRelease(UTI);
if (!MIMEType) {
return
@"application/octet-stream";
}
return (__bridge
NSString *)(MIMEType);
}
**************************黑馬筆記****************************
一、大文件下載
1.方案:利用NSURLConnection和它的代理方法
1> 發送一個請求
1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/videos.zip"];
2.請求
NSURLRequest *request
= [NSURLRequest requestWithURL:url];
3.下載(創建完conn對象后,會自動發起一個異步請求)
[NSURLConnection
connectionWithRequest:request delegate:self];
2> 在代理方法中處理服務器返回的數據
在接收到服務器的響應時:
1.創建一個空的文件
2.用一個句柄對象關聯這個空的文件,目的是:方便后面用句柄對象往文件后面寫數據
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
文件路徑
NSString
*caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES) lastObject];
NSString
*filepath = [caches stringByAppendingPathComponent:@"videos.zip"];
創建一個空的文件 到 沙盒中
NSFileManager *mgr = [NSFileManager defaultManager];
[mgr createFileAtPath:filepath contents:nil
attributes:nil];
創建一個用來寫數據的文件句柄
self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath];
}
在接收到服務器返回的文件數據時,利用句柄對象往文件的最后面追加數據
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
移動到文件的最后面
[self.writeHandle seekToEndOfFile];
將數據寫入沙盒
[self.writeHandle writeData:data];
}
在所有數據接收完畢時,關閉句柄對象
- (void)connectionDidFinishLoading:(NSURLConnection
*)connection
{
關閉文件
[self.writeHandle closeFile];
self.writeHandle = nil;
}
2.注意點:千萬不能用NSMutableData來拼接服務器返回的數據
二、NSURLConnection發送異步請求的方法
1.block形式
- 除開大文件下載以外的操作,都可以用這種形式
[NSURLConnection
sendAsynchronousRequest:<#(NSURLRequest *)#> queue:<#(NSOperationQueue
*)#> completionHandler:^(NSURLResponse *response, NSData *data, NSError
*connectionError) {
}];
2.代理形式
- 一般用在大文件下載
1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/login?username=123&pwd=123"];
2.請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
3.下載(創建完conn對象后,會自動發起一個異步請求)
[NSURLConnection connectionWithRequest:request delegate:self];
三、NSURLSession
1.使用步驟
1> 獲得NSURLSession對象
2> 利用NSURLSession對象創建對應的任務(Task)
3> 開始任務([task
resume])
2.獲得NSURLSession對象
1> [NSURLSession sharedSession]
2>NSURLSessionConfiguration
*cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession
sessionWithConfiguration:cfg delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
3.任務類型
1>
NSURLSessionDataTask
- 用途:用于非文件下載的GET\POST請求
NSURLSessionDataTask
*task = [self.session
dataTaskWithRequest:request];
NSURLSessionDataTask *task = [self.session dataTaskWithURL:url];
NSURLSessionDataTask *task = [self.session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
2>
NSURLSessionDownloadTask
- 用途:用于文件下載(小文件、大文件)
NSURLSessionDownloadTask *task = [self.session
downloadTaskWithRequest:request];
NSURLSessionDownloadTask *task = [self.session
downloadTaskWithURL:url];
NSURLSessionDownloadTask *task = [self.session
downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
}];