七牛云存储文件上传与下载
戴维营教育原创文章,转载请注明出处。我们的梦想是做最好的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操作方式来下载图片了。
最后祝大家端午节快乐,多吃粽子~~~