七牛云存储文件上传与下载

戴维营教育原创文章,转载请注明出处。我们的梦想是做最好的iOS开发培训!

“陪你”校园陌生人交友APP中使用了七牛云存储,因此关注了一下这个人气很高的公有云平台的使用。七牛使用K-V的方式存放文具。在上传文件的时候,需要保证文件名是唯一的,当然,也可以不提供文件名(key),系统会自动将文件的MD5作为key。但是有一个问题,这样的话,不同用户就不能够上传相同的图片了,因为HASH值是一样的,上传失败。七牛给了我们一套使用上传策略(PutPolicy)计算上传令牌(UploadToken)的方法,下载同样需要下载令牌。这些值都是通过七牛提供的AccessKey和SecretKey计算出来的。Token的计算可以在客户端也可以在服务器端,但是为了安全起见,我们一般会将Token的生成过程放在服务器,而不是放在客户端。

上传令牌的计算(UploadToken)

为了方便演示,顺便熟悉一下这些Token的计算过程,我们将它放在客户端(iOS)进行。七牛将上传令牌的计算分为七步:

1 . Key(文件名)的生成

这一步本来也是在服务器上生成的,为了保证Key的唯一性,我们采用“陪你ID+当前时间戳+随机数”的方式生成。
+ (NSString *)keyForUpload:(NSString *)userId {
    //当前UNIX系统时间戳
    NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
    return [NSString stringWithFormat:@"%@%.0f%u", userId, timestamp, arc4random()];
}

2 . 上传策略Scope的生成

七牛规定Scope必须包含我们申请的空间号。如果需要指定文件名,则应该附加Key值在里面。最后还应该有这个Token的超时时间deadline。
{"scope":"peini:key","deadline":1434198873}

我们在OC中可以通过字典并且JSON序列化得到这个字符串。

//有效时间为当前时间开始后1个小时以内,必须使用UNIX时间戳
NSInteger deadline = [[NSDate dateWithTimeIntervalSinceNow:3600] timeIntervalSince1970];
NSDictionary *dict = @{
        @"scope":[NSString stringWithFormat:@"peiniliao:%@", key],
        @"deadline":@(deadline)
};

3 . 序列化Scope

将上面的字典序列化为JSON字符串,但是需要注意,不能使用PrettyPrint方式序列化,那样会在字符串中增加许多空格和回车,从而使Token的生成失败。
//切记不需要留空格和回车换行,options为0表示不需要改善JSON的打印样式
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:putPolicy options:0 error:nil];

4 . Base64编码序列化后的Scope

因为URL中不能出现二进制数据,因此需要进行Base64编码,但是为了安全起见,七牛在SDK中引入了所谓的安全的Base64编码。

NSString *encodedPutPolicy = [QNUrlSafeBase64 encodeData:jsonData];

5 . Hmac-Sha1加密签名

苹果官方替我们实现了常用的加密和摘要算法,如MD5、SHA1等,但是提供的是C语言的接口,为了方便起见,我们使用了一个开源的CocoaSecurity框架。它帮我们用OC封装了这些常用算法。需要使用SecretKey进行加密,SecretKey一般是不能在客户端泄露出去的,可能导致安全问题。

CocoaSecurityResult *result = [CocoaSecurity hmacSha1:encodedPutPolicy hmacKey:(NSString * const)kSecretKey];

6 . Base64编码签名数据

加密得到的二进制数据还得进一步Base64编码转换为文本信息才能进行传输。

NSString *encodedSign = [QNUrlSafeBase64 encodeData:result.data];

7 . 拼接UploadToken

[NSString stringWithFormat:@"%@:%@:%@", kAccessKey, encodedSign, encodedPutPolicy];

七牛SDK上传文件

//生成上传token
NSString *uploadToken = [DVIDataManager uploadToken:dict];
NSLog(@"uploadToken: %@", uploadToken);

QNUploadManager *upManager = [[QNUploadManager alloc] init];

NSString *path = [[NSBundle mainBundle] pathForResource:@"meinv" ofType:@"jpg"];
//获取文件内容
NSData *data = [NSData dataWithContentsOfFile:path];

//使用key和token上传文件
[upManager putData:data key:key token:uploadToken
          complete: ^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
              NSLog(@"%@", info);
              NSLog(@"%@", resp);

              NSLog(@"%@", [DVIDataManager downloadPathForKey:key]);
} option:nil];

下载令牌的计算

从七牛下载文件同样需要计算下载令牌(公开的资源部需要)。

+ (NSString *)downloadPathForKey:(NSString *)key {
    //下载令牌的失效时间
    NSInteger deadline = [[NSDate dateWithTimeIntervalSinceNow:3600] timeIntervalSince1970];

    NSString *downloadURL = [NSString stringWithFormat:@"http://空间的域名(七牛分配了二级域名,也可以绑定自己的)/%@?e=%ld", key, deadline];

    //加密
    CocoaSecurityResult *result = [CocoaSecurity hmacSha1:downloadURL hmacKey:(NSString * const)kSecretKey];

    NSString *encodedSign = [QNUrlSafeBase64 encodeData:result.data];//[result base64];

    //拼接令牌
    NSString *downloadToken = [NSString stringWithFormat:@"%@:%@", kAccessKey, encodedSign];

    //拼接下载URL
    return [NSString stringWithFormat:@"%@&token=%@", downloadURL, downloadToken];
}

文件下载

拿到上一步计算出来的URL后,就可以随意使用自己喜欢的HTTP操作方式来下载图片了。

最后祝大家端午节快乐,多吃粽子~~~

戴维营学院(高级开发视频): http://v.diveinedu.com

潜心俱乐部(iOS面试必备): http://divein.club