Last active
August 29, 2015 14:07
-
-
Save VincentSit/685ce9c260395f06b44f to your computer and use it in GitHub Desktop.
阿里云 OSS 上传&下载文件。iOS 7+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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