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的格式,但從目前看來可能性並不大,因為如果不向前相容,就會意味著以前舊的格式無法使用,這是我們不能容忍的 :)