Just My Life & My Work

[iOS] 下載超大檔案

下載超大檔案下載小檔案有何不同?同樣是下載的動作,這時候就要考慮到時間空間

IMG_3986

若考慮時間,用3G下載通常比用WiFi下載慢很多,於是下載時間就會拉長,只要有足夠的時間一樣可以下載完畢,然而問題在於下載的狀態又分前景模式背景模式,前景模式下載只要設備有足夠電源就能完成下載,背景模式下載就要考慮要在限定的時間內下載完畢。可以參考我剛寫的文章:爭取背景執行時間

若考慮空間硬碟當然要有足夠的空間可容納檔案,主要的議題是在於從網路傳輸資料,會先暫存於記憶體,而iOS分配給每個APP記憶體有限,以目前iPhone記憶體1G的情況來看,一個APP能獲得500MB的額度就很多了,若我想下載一個超過500MB的檔案就有記憶體會爆掉的危機,那我們可以怎麼做呢?

我這裡的做法是,每當delegate取得些許資料的時候,就將記憶體裡的資料儲存到硬碟中,陸續將之後的資料附加於已存在硬碟中檔案的後方,逐漸拼成一個完整的檔案,如此記憶體使用量就能維持在低量。

我找到兩種方法可用,法一:

/**
 Theme: Download big file
 IDE: Xcode 5
 Language: Objective C
 Date: 103/04/09
 Author: HappyMan
 Blog: https://cg2010studio.wordpress.com/
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSOutputStream *stream = [[NSOutputStream alloc] initToFileAtPath:downloadTargetPath append:YES];
    [stream open];
    NSUInteger left = [data length];
    NSUInteger nwr = 0;
    do {
        nwr = [stream write:[data bytes] maxLength:left];
        if (-1 == nwr) break;
        left -= nwr;
    } while (left > 0);
    if (left) {
        NSLog(@"stream error: %@", [stream streamError]);
    }
    [stream close];
}

上頭的方法有點奇妙,我們來看法二:

/**
 Theme: Download big file
 IDE: Xcode 5
 Language: Objective C
 Date: 103/04/09
 Author: HappyMan
 Blog: https://cg2010studio.wordpress.com/
 */
// HTFileManager.h
@interface HTFileManager : NSObject
{
    NSFileHandle *fileHandle;
}
// HTFileManager.m
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    fileHandle = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if(fileHandle) {
        // 找到檔案的尾巴
        [fileHandle seekToEndOfFile];
    }
    else {
        // 建立檔案的URL
        [[NSFileManager defaultManager] createFileAtPath:downloadTargetPath contents:nil attributes:nil];
        // 初始化檔案處理
        fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:downloadTargetPath];
    }
    // 寫入資料到硬碟
    [fileHandle writeData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // 關閉檔案處理
    [fileHandle closeFile];
}

看來上述程式碼一看就懂!然而兩者有異曲同工之妙,效果差不多~

這裡有三個delegate,下載檔案陸續會執行:

  1. didReceiveResponse (一次)
  2. didReceiveData (多次)
  3. didFinishLoading (一次)

接下來我們怎麼知道,網路傳輸的資料真的有從記憶體轉換到硬碟中?好,可以開啟Xcode提供的工具Instruments,實際去執行APP,然後下載超大檔案試試看!

Instruments big file

可以見到,最後記憶體使用量是88MB,然而期間總共使用記憶體高達39GB。

最後想說的是,使用不同的工具來檢測效果是較好的做法,我在Xcode編譯好在實機模擬器上跑,Debug Gauges顯示的記憶體使用量會不斷往上攀升,然而同樣的測試過程,使用Instruments就沒有令人無奈的現象,是比較符合使用者真實情況。

Debug Gauges

Debug Gauges可以見到Memory的目前的使用狀況。

參考:Downloading a Large File – iPhone SDKHow to download a large file with the iPhone SDK and avoid memory usage issues?

Comments on: "[iOS] 下載超大檔案" (2)

  1. […] 像是超大影片(例如100MB)要下載,就不能先全部載到記憶體,再轉存到硬碟,而是要每下載一小部分就從記憶體轉存到硬碟,以免超過App所能乘載的記憶體量而崩潰。 […]

  2. […] 像是超大影片(例如100MB)要下載,就不能先全部載到記憶體,再轉存到硬碟,而是要每下載一小部分就從記憶體轉存到硬碟,以免超過App所能乘載的記憶體量而崩潰。 […]

隨意留個言吧:)~

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料

標籤雲