Skip to content

Instantly share code, notes, and snippets.

@VincentSit
Last active August 29, 2015 14:07
Show Gist options
  • Save VincentSit/685ce9c260395f06b44f to your computer and use it in GitHub Desktop.
Save VincentSit/685ce9c260395f06b44f to your computer and use it in GitHub Desktop.
阿里云 OSS 上传&下载文件。iOS 7+
EOTEXTERN NSString * const EOTAliyunOSSClientErrorDomain;
@interface EOTAliyunOSSClient : NSObject
+ (instancetype)sharedClient;
- (void)getObjectWithPath:(NSString *)path
inBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)(NSData *responseData))success
failure:(void (^)(NSError *error))failure;
- (void)postObjectWithData:(NSData *)data
destinationPath:(NSString *)destinationPath
toBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)())success
failure:(void (^)(NSError *error))failure;
- (void)postObjectWithFile:(NSString *)path
destinationPath:(NSString *)destinationPath
toBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)())success
failure:(void (^)(NSError *error))failure;
@end
#import "EOTAliyunOSSClient.h"
#import <CommonCrypto/CommonHMAC.h>
NSString * const EOTAliyunOSSClientErrorDomain = @"com.eastopentech.spofie.aliyunoss.error";
NSString * const EOTAliyunOSSHangzhouRegion = @"oss-cn-hangzhou.aliyuncs.com";
// From https://github.com/AFNetworking/AFAmazonS3Client/blob/master/AFAmazonS3Client/AFAmazonS3RequestSerializer.m#L37
static NSData * EOTHMACSHA1EncodedDataFromStringWithKey(NSString *string, NSString *key) {
NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
CCHmacContext context;
const char *keyCString = [key cStringUsingEncoding:NSASCIIStringEncoding];
CCHmacInit(&context, kCCHmacAlgSHA1, keyCString, strlen(keyCString));
CCHmacUpdate(&context, [data bytes], [data length]);
unsigned char digestRaw[CC_SHA1_DIGEST_LENGTH];
NSUInteger digestLength = CC_SHA1_DIGEST_LENGTH;
CCHmacFinal(&context, digestRaw);
return [NSData dataWithBytes:digestRaw length:digestLength];
}
static NSString * EOTRFC822FormatStringFromDate(NSDate *date) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
[dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
return [dateFormatter stringFromDate:date];
}
@interface EOTAliyunOSSClient ()
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSMutableSet *requestCacheSet;
@end
@implementation EOTAliyunOSSClient
- (NSURLSession *)session {
if (!_session) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}
return _session;
}
- (NSMutableSet *)requestCacheSet {
if (!_requestCacheSet) {
_requestCacheSet = [NSMutableSet set];
}
return _requestCacheSet;
}
+ (instancetype)sharedClient {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (void)cancelAllRequests {
[self.session invalidateAndCancel];
}
- (void)getObjectWithPath:(NSString *)path
inBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)(NSData *responseData))success
failure:(void (^)(NSError *error))failure {
NSParameterAssert(path && ![path eot_isEmpty]);
NSParameterAssert([EOTCloudStorageManager sharedManager].appKeyID);
NSParameterAssert([EOTCloudStorageManager sharedManager].appKey);
NSString *bucketName = [[EOTCloudStorageManager sharedManager] bucketNamedWithType:bucket];
NSString *cacheKey = [bucketName stringByAppendingPathComponent:path];
if ([self.requestCacheSet containsObject:cacheKey]) {
DDLogDebug(@"重复下载");
return;
}
NSError *requestError = nil;
NSURLRequest *request = [self requestWithMethod:@"GET" filePath:path bucket:bucketName error:&requestError];
if (!requestError) {
[self.requestCacheSet addObject:cacheKey];
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
[self.requestCacheSet removeObject:cacheKey];
NSData *data = [NSData dataWithContentsOfURL:location];
if (!error && [data length] > 1024) {
!success ?: success(data);
}
else {
!failure ?: failure(error);
}
}];
[downloadTask resume];
}
else {
!failure ?: failure(requestError);
}
}
- (void)postObjectWithData:(NSData *)data
destinationPath:(NSString *)destinationPath
toBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)())success
failure:(void (^)(NSError *error))failure {
NSParameterAssert(data);
NSParameterAssert(destinationPath && ![destinationPath eot_isEmpty]);
NSParameterAssert([EOTCloudStorageManager sharedManager].appKeyID);
NSParameterAssert([EOTCloudStorageManager sharedManager].appKey);
NSString *bucketName = [[EOTCloudStorageManager sharedManager] bucketNamedWithType:bucket];
NSString *cacheKey = [bucketName stringByAppendingPathComponent:destinationPath];
if ([self.requestCacheSet containsObject:cacheKey]) {
DDLogDebug(@"重复上传");
return;
}
NSError *requestError = nil;
NSURLRequest *request = [self requestWithMethod:@"POST" filePath:destinationPath bucket:bucketName error:&requestError];
if (!requestError) {
[self.requestCacheSet addObject:cacheKey];
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:data
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[self.requestCacheSet removeObject:cacheKey];
if (!error) {
!success ?: success();
}
else {
!failure ?: failure(error);
}
}];
[uploadTask resume];
}
else {
!failure ?: failure(requestError);
}
}
- (void)postObjectWithFile:(NSString *)path
destinationPath:(NSString *)destinationPath
toBucket:(EOTCloudStorageBucketType)bucket
success:(void (^)())success
failure:(void (^)(NSError *error))failure {
NSMutableURLRequest *fileRequest = [NSMutableURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
[fileRequest setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
NSURLResponse *response = nil;
NSError *fileError = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:fileRequest returningResponse:&response error:&fileError];
if (data && response) {
[self postObjectWithData:data destinationPath:destinationPath toBucket:bucket success:success failure:failure];
}
else {
!failure ?: failure(fileError);
}
}
- (NSURLRequest *)requestWithMethod:(NSString *)method
filePath:(NSString *)filePath
bucket:(NSString *)bucket
error:(NSError *__autoreleasing *)error {
NSParameterAssert(method);
NSParameterAssert(filePath);
NSString *URLString = [NSString stringWithFormat:@"http://%@.%@/%@", bucket, EOTAliyunOSSHangzhouRegion, filePath];
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
mutableRequest.timeoutInterval = 60.f;
NSString *accessKey = [EOTCloudStorageManager sharedManager].appKeyID;
NSString *secret = [EOTCloudStorageManager sharedManager].appKey;
if (accessKey && secret) {
NSString *method = [mutableRequest HTTPMethod];
NSString *contentMD5 = [mutableRequest valueForHTTPHeaderField:@"Content-MD5"];
NSString *contentType = [mutableRequest valueForHTTPHeaderField:@"Content-Type"];
NSString *date = EOTRFC822FormatStringFromDate([NSDate date]);
NSParameterAssert(date);
NSString *canonicalizedResource = [NSString stringWithFormat:@"/%@%@", bucket, mutableRequest.URL.path];
NSParameterAssert(canonicalizedResource);
NSMutableString *mutableString = [NSMutableString string];
[mutableString appendFormat:@"%@\n", (method) ? method : @""];
[mutableString appendFormat:@"%@\n", (contentMD5) ? contentMD5 : @""];
[mutableString appendFormat:@"%@\n", (contentType) ? contentType : @""];
[mutableString appendFormat:@"%@\n", (date) ? date : @""];
[mutableString appendFormat:@"%@", canonicalizedResource];
NSData *hmac = EOTHMACSHA1EncodedDataFromStringWithKey(mutableString, secret);
NSString *signature = [hmac base64EncodedStringWithOptions:0];
NSMutableDictionary *header = [NSMutableDictionary dictionary];
[header setValue:[NSString stringWithFormat:@"OSS %@:%@", accessKey, signature] forKey:@"Authorization"];
[header setValue:(date) ? date : @"" forKey:@"Date"];
mutableRequest.allHTTPHeaderFields = header;
} else {
if (error) {
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Access Key and Secret Required", nil)};
*error = [[NSError alloc] initWithDomain:EOTAliyunOSSClientErrorDomain code:NSURLErrorUserAuthenticationRequired userInfo:userInfo];
}
}
return mutableRequest;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment