1. 程式人生 > >iOS獲取iCloud檔案實際大小的方法

iOS獲取iCloud檔案實際大小的方法

我們知道在iCloud檔案未同步到本地之前,它實際上只是一個佔位檔案。

如果iCloud檔名為look.pdf,那麼實際本地的檔案是.look.pdf.icloud,儘管你實際在Find中看到的貌似是look.pdf。

實際上.look.pdf.icloud是一個二進位制的plist檔案(bplist),但遺憾的是bplist格式Apple並沒有公佈出來!

那麼如何獲取該檔案的實際大小呢?

主要有兩種方法:

1.使用public link來實現
2.解析bplist檔案來實現

第一種方法很方便,但生成public link速度慢,給使用者體驗不佳。

這裡主要聊一聊後面一種方法。雖說bplist檔案格式是不透明的,但所幸的是我們沒必要解析整個bplist檔案,我們只需要將檔案大小解析出來。

下面是雲檔案在本地實際的內容:

在這裡插入圖片描述

可以看到它以bplist00開頭,然後包含3個Key和他們對應的Value,這3個Key其中就有云檔案的大小:NSURLFileSizeKey.但是它對應的值在哪裡呢?

通過逆向bplist檔案格式,從其中反向查詢大小位元組,可以知道其值就放在檔名與_下劃線字元中間的位置。

注意檔案大小儲存的位元組是可變的,如果不需要不會用4位元組來儲存(我不知道是否會用8位元組,但目前來說4位元組足夠了,因為誰也不會上傳超過40GB的單個檔案到雲上去)。關鍵要看開頭和結尾的位置。

因為要操作二進位制資料,並且需要將二進位制位元組轉換為UInt(或Int)型別,所以最好是寫一個ObjC方法來完成:

///取得雲檔案的大小,通過傳入本地的bplist檔案.
+(int)getCloudBplistFileSize:(NSString*)bplistFilePath{
    //需要用ascii編碼而不是utf8,否則開啟檔案會失敗
    NSString* content = [NSString stringWithContentsOfFile:bplistFilePath encoding:NSASCIIStringEncoding error:nil];
    if(content == nil){return 0;}
    //將本地bplist檔名稱轉換為雲中檔名稱
    NSString *cloudFileName = [self cloudFileNameFor:bplistFilePath.lastPathComponent];
    //找到雲檔名所在的range,並由此建立搜尋"_"字元的range
    NSRange range = [content rangeOfString:cloudFileName];
    int startIdx = (int)(range.location + range.length + 1);
    NSRange findRange = NSMakeRange(startIdx, content.length - startIdx);
    //找到"_"字元的偏移,從而找到size 4位元組所在的偏移
    NSRange _range = [content rangeOfString:@"_" options:NSLiteralSearch range:findRange];
    NSRange sizeValueRange = NSMakeRange(startIdx, _range.location - startIdx);
    NSString *sizeString = [content substringWithRange:sizeValueRange];
    
    int index = 0;
    char ary[4] = {0};
    //NSString中實際存放每個位元組使用16位(高8位都是0),所以要將其轉換為緊湊的8位陣列.
    while (index < sizeString.length) {
        char c = [sizeString characterAtIndex:index];
        //intel小尾模式:高位在低位,地位在高位,需要反轉位元組.
        ary[3-index] = c;
        index++;
    }
    //建立指向size位元組陣列的Int型指標
    int *pSize = (int*)ary;
    return *pSize;
}

上面我做了較詳細的註釋,應該沒什麼問題。其中一個需要理解的地方是在Intel平臺上,CPU對於記憶體位元組的解析採用小尾模式,如果之前沒接觸過組合語言的童鞋可能不是太瞭解。不過大家知道要將位元組翻轉過來就可以了。(但暫未測試真機上的位元組順序,如果是大尾,則不需要翻轉位元組!這個可以通過編譯巨集來處理。)

將上述方法通過全域性方法或類例項方法的形式放在Static Lib中,同時設定好正確的標頭檔案格式,我們就可以在Swift專案中使用它了。

不過如果完全用純Swift能不能實現上述功能呢?答案當然是肯定的,但是由於Swift語言本身的限制,程式碼比較長,這裡就不貼出了。

總結一下:對於某些需要從本地獲取未同步iCloud雲檔案大小的需求來說,直接解析本地bplist檔案從速度和穩定性來說都十分可觀,除非Apple將來變更bplist的格式,但從目前看來可能性並不大,因為如果不向前相容,就會意味著以前舊的格式無法使用,這是我們不能容忍的 :)