iOS获取iCloud文件实际大小的方法

版权声明:大熊猫猪·侯佩原创或翻译作品.谢绝转载! hopy https://blog.csdn.net/mydo/article/details/84197576

我们知道在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.但是它对应的值在哪里呢?

扫描二维码关注公众号,回复: 4353866 查看本文章

通过逆向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的格式,但从目前看来可能性并不大,因为如果不向前兼容,就会意味着以前旧的格式无法使用,这是我们不能容忍的 :)

猜你喜欢

转载自blog.csdn.net/mydo/article/details/84197576