add .gitignore

This commit is contained in:
JoyWayer
2023-12-27 20:38:37 +08:00
parent b106a628a5
commit f6343426d6
515 changed files with 104217 additions and 199 deletions

View File

@@ -0,0 +1,205 @@
//
// QNConfiguration.h
// QiniuSDK
//
// Created by bailong on 15/5/21.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNRecorderDelegate.h"
/**
* 断点上传时的分块大小
*/
extern const UInt32 kQNBlockSize;
/**
* 转换为用户需要的url
*
* @param url 上传url
*
* @return 根据上传url算出代理url
*/
typedef NSString * (^QNUrlConvert)(NSString *url);
@class QNConfigurationBuilder;
@class QNDnsManager;
@class QNServiceAddress;
@class QNZone;
/**
* Builder block
*
* @param builder builder实例
*/
typedef void (^QNConfigurationBuilderBlock)(QNConfigurationBuilder *builder);
@interface QNConfiguration : NSObject
/**
* 存储区域
*/
@property (copy, nonatomic, readonly) QNZone *zone;
/**
* 断点上传时的分片大小
*/
@property (readonly) UInt32 chunkSize;
/**
* 如果大于此值就使用断点上传否则使用form上传
*/
@property (readonly) UInt32 putThreshold;
/**
* 上传失败的重试次数
*/
@property (readonly) UInt32 retryMax;
/**
* 超时时间 单位 秒
*/
@property (readonly) UInt32 timeoutInterval;
@property (nonatomic, readonly) id<QNRecorderDelegate> recorder;
@property (nonatomic, readonly) QNRecorderKeyGenerator recorderKeyGen;
@property (nonatomic, readonly) NSDictionary *proxy;
@property (nonatomic, readonly) QNUrlConvert converter;
@property (nonatomic, readonly) QNDnsManager *dns;
@property (readonly) BOOL disableATS;
+ (instancetype)build:(QNConfigurationBuilderBlock)block;
@end
/**
* 上传服务地址
*/
@interface QNServiceAddress : NSObject
- (instancetype)init:(NSString *)address ips:(NSArray *)ips;
@property (nonatomic, readonly) NSString *address;
@property (nonatomic, readonly) NSArray *ips;
@end
typedef void (^QNPrequeryReturn)(int code);
@class QNUpToken;
@interface QNZone : NSObject
/**
* 默认上传服务器地址
*/
- (QNServiceAddress *)up:(QNUpToken *)token;
/**
* 备用上传服务器地址
*/
- (QNServiceAddress *)upBackup:(QNUpToken *)token;
/**
* zone 0 华东
*
* @return 实例
*/
+ (instancetype)zone0;
/**
* zone 1 华北
*
* @return 实例
*/
+ (instancetype)zone1;
/**
* zone 2 华南
*
* @return 实例
*/
+ (instancetype)zone2;
/**
* zone Na0 北美
*
* @return 实例
*/
+ (instancetype)zoneNa0;
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret;
+ (void)addIpToDns:(QNDnsManager *)dns;
@end
@interface QNFixedZone : QNZone
/**
* Zone初始化方法
*
* @param upHost 默认上传服务器地址
* @param upHostBackup 备用上传服务器地址
* @param upIp 备用上传IP
*
* @return Zone实例
*/
- (instancetype)initWithUp:(QNServiceAddress *)up
upBackup:(QNServiceAddress *)upBackup;
@end
@interface QNAutoZone : QNZone
- (instancetype)initWithHttps:(BOOL)flag
dns:(QNDnsManager *)dns;
@end
@interface QNConfigurationBuilder : NSObject
/**
* 默认上传服务器地址
*/
@property (nonatomic, strong) QNZone *zone;
/**
* 断点上传时的分片大小
*/
@property (assign) UInt32 chunkSize;
/**
* 如果大于此值就使用断点上传否则使用form上传
*/
@property (assign) UInt32 putThreshold;
/**
* 上传失败的重试次数
*/
@property (assign) UInt32 retryMax;
/**
* 超时时间 单位 秒
*/
@property (assign) UInt32 timeoutInterval;
@property (nonatomic, strong) id<QNRecorderDelegate> recorder;
@property (nonatomic, strong) QNRecorderKeyGenerator recorderKeyGen;
@property (nonatomic, strong) NSDictionary *proxy;
@property (nonatomic, strong) QNUrlConvert converter;
@property (nonatomic, strong) QNDnsManager *dns;
@property (assign) BOOL disableATS;
@end

View File

@@ -0,0 +1,360 @@
//
// QNConfiguration.m
// QiniuSDK
//
// Created by bailong on 15/5/21.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNConfiguration.h"
#import "HappyDNS.h"
#import "QNNetworkInfo.h"
#import "QNResponseInfo.h"
#import "QNSessionManager.h"
#import "QNSystem.h"
#import "QNUpToken.h"
const UInt32 kQNBlockSize = 4 * 1024 * 1024;
static void addServiceToDns(QNServiceAddress *address, QNDnsManager *dns) {
NSArray *ips = address.ips;
if (ips == nil) {
return;
}
NSURL *u = [[NSURL alloc] initWithString:address.address];
NSString *host = u.host;
for (int i = 0; i < ips.count; i++) {
[dns putHosts:host ip:ips[i]];
}
}
static void addZoneToDns(QNZone *zone, QNDnsManager *dns) {
addServiceToDns([zone up:nil], dns);
addServiceToDns([zone upBackup:nil], dns);
}
static QNDnsManager *initDns(QNConfigurationBuilder *builder) {
QNDnsManager *d = builder.dns;
if (d == nil) {
id<QNResolverDelegate> r1 = [QNResolver systemResolver];
id<QNResolverDelegate> r2 = [[QNResolver alloc] initWithAddress:@"119.29.29.29"];
id<QNResolverDelegate> r3 = [[QNResolver alloc] initWithAddress:@"114.114.115.115"];
d = [[QNDnsManager alloc] init:[NSArray arrayWithObjects:r1, r2, r3, nil] networkInfo:[QNNetworkInfo normal]];
}
return d;
}
@implementation QNConfiguration
+ (instancetype)build:(QNConfigurationBuilderBlock)block {
QNConfigurationBuilder *builder = [[QNConfigurationBuilder alloc] init];
block(builder);
return [[QNConfiguration alloc] initWithBuilder:builder];
}
- (instancetype)initWithBuilder:(QNConfigurationBuilder *)builder {
if (self = [super init]) {
_chunkSize = builder.chunkSize;
_putThreshold = builder.putThreshold;
_retryMax = builder.retryMax;
_timeoutInterval = builder.timeoutInterval;
_recorder = builder.recorder;
_recorderKeyGen = builder.recorderKeyGen;
_proxy = builder.proxy;
_converter = builder.converter;
_disableATS = builder.disableATS;
if (_disableATS) {
_dns = initDns(builder);
[QNZone addIpToDns:_dns];
} else {
_dns = nil;
}
_zone = builder.zone;
}
return self;
}
@end
@implementation QNConfigurationBuilder
- (instancetype)init {
if (self = [super init]) {
_zone = [QNZone zone0];
_chunkSize = 256 * 1024;
_putThreshold = 512 * 1024;
_retryMax = 2;
_timeoutInterval = 60;
_recorder = nil;
_recorderKeyGen = nil;
_proxy = nil;
_converter = nil;
if (hasAts() && !allowsArbitraryLoads()) {
_disableATS = NO;
} else {
_disableATS = YES;
}
}
return self;
}
@end
@implementation QNServiceAddress : NSObject
- (instancetype)init:(NSString *)address ips:(NSArray *)ips {
if (self = [super init]) {
_address = address;
_ips = ips;
}
return self;
}
@end
@implementation QNFixedZone {
QNServiceAddress *up;
QNServiceAddress *upBackup;
}
/**
*
*/
- (QNServiceAddress *)upBackup:(NSString *)token {
return upBackup;
}
- (QNServiceAddress *)up:(NSString *)token {
return up;
}
- (instancetype)initWithUp:(QNServiceAddress *)up1
upBackup:(QNServiceAddress *)upBackup1 {
if (self = [super init]) {
up = up1;
upBackup = upBackup1;
}
return self;
}
@end
@interface QNAutoZoneInfo : NSObject
@property (readonly, nonatomic) NSString *upHost;
@property (readonly, nonatomic) NSString *upIp;
@property (readonly, nonatomic) NSString *upBackup;
@property (readonly, nonatomic) NSString *upHttps;
- (instancetype)init:(NSString *)uphost
upIp:(NSString *)upip
upBackup:(NSString *)upbackup
upHttps:(NSString *)uphttps;
@end
@implementation QNAutoZoneInfo
- (instancetype)init:(NSString *)uphost
upIp:(NSString *)upip
upBackup:(NSString *)upbackup
upHttps:(NSString *)uphttps {
if (self = [super init]) {
_upHost = uphost;
_upIp = upip;
_upBackup = upbackup;
_upHttps = uphttps;
}
return self;
}
@end
@implementation QNAutoZone {
NSString *server;
BOOL https;
NSMutableDictionary *cache;
NSLock *lock;
QNSessionManager *sesionManager;
QNDnsManager *dns;
}
- (instancetype)initWithHttps:(BOOL)flag
dns:(QNDnsManager *)dns1 {
if (self = [super init]) {
dns = dns1;
server = @"https://uc.qbox.me";
https = flag;
cache = [NSMutableDictionary new];
lock = [NSLock new];
sesionManager = [[QNSessionManager alloc] initWithProxy:nil timeout:10 urlConverter:nil dns:dns];
}
return self;
}
- (QNServiceAddress *)upBackup:(QNUpToken *)token {
NSString *index = [token index];
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:index];
[lock unlock];
if (info == nil) {
return nil;
}
if (https) {
return [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
}
return [[QNServiceAddress alloc] init:info.upBackup ips:@[ info.upIp ]];
}
- (QNServiceAddress *)up:(QNUpToken *)token {
NSString *index = [token index];
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:index];
[lock unlock];
if (info == nil) {
return nil;
}
if (https) {
return [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
}
return [[QNServiceAddress alloc] init:info.upHost ips:@[ info.upIp ]];
}
- (QNAutoZoneInfo *)buildInfoFromJson:(NSDictionary *)resp {
NSDictionary *http = [resp objectForKey:@"http"];
NSArray *up = [http objectForKey:@"up"];
NSString *upHost = [up objectAtIndex:1];
NSString *upBackup = [up objectAtIndex:0];
NSString *ipTemp = [up objectAtIndex:2];
NSArray *a1 = [ipTemp componentsSeparatedByString:@" "];
NSString *ip1 = [a1 objectAtIndex:2];
NSArray *a2 = [ip1 componentsSeparatedByString:@"//"];
NSString *upIp = [a2 objectAtIndex:1];
NSDictionary *https_ = [resp objectForKey:@"https"];
NSArray *a3 = [https_ objectForKey:@"up"];
NSString *upHttps = [a3 objectAtIndex:0];
return [[QNAutoZoneInfo alloc] init:upHost upIp:upIp upBackup:upBackup upHttps:upHttps];
}
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret {
if (token == nil) {
ret(-1);
}
[lock lock];
QNAutoZoneInfo *info = [cache objectForKey:[token index]];
[lock unlock];
if (info != nil) {
ret(0);
return;
}
NSString *url = [NSString stringWithFormat:@"%@/v1/query?ak=%@&bucket=%@", server, token.access, token.bucket];
[sesionManager get:url withHeaders:nil withCompleteBlock:^(QNResponseInfo *info, NSDictionary *resp) {
if ([info isOK]) {
QNAutoZoneInfo *info = [self buildInfoFromJson:resp];
if (info == nil) {
ret(kQNInvalidToken);
} else {
ret(0);
[lock lock];
[cache setValue:info forKey:[token index]];
[lock unlock];
if (dns != nil) {
QNServiceAddress *address = [[QNServiceAddress alloc] init:info.upHttps ips:@[ info.upIp ]];
addServiceToDns(address, dns);
address = [[QNServiceAddress alloc] init:info.upHost ips:@[ info.upIp ]];
addServiceToDns(address, dns);
address = [[QNServiceAddress alloc] init:info.upBackup ips:@[ info.upIp ]];
addServiceToDns(address, dns);
}
}
} else {
ret(kQNNetworkError);
}
}];
}
@end
@implementation QNZone
- (instancetype)init {
self = [super init];
return self;
}
/**
*
*/
- (QNServiceAddress *)upBackup:(QNUpToken *)token {
return nil;
}
- (QNServiceAddress *)up:(QNUpToken *)token {
return nil;
}
+ (instancetype)createWithHost:(NSString *)up backupHost:(NSString *)backup ip1:(NSString *)ip1 ip2:(NSString *)ip2 {
NSArray *ips = [NSArray arrayWithObjects:ip1, ip2, nil];
NSString *a = [NSString stringWithFormat:@"http://%@", up];
QNServiceAddress *s1 = [[QNServiceAddress alloc] init:a ips:ips];
NSString *b = [NSString stringWithFormat:@"http://%@", backup];
QNServiceAddress *s2 = [[QNServiceAddress alloc] init:b ips:ips];
return [[QNFixedZone alloc] initWithUp:s1 upBackup:s2];
}
+ (instancetype)zone0 {
static QNZone *z0 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z0 = [QNZone createWithHost:@"upload.qiniu.com" backupHost:@"up.qiniu.com" ip1:@"183.136.139.10" ip2:@"115.231.182.136"];
});
return z0;
}
+ (instancetype)zone1 {
static QNZone *z1 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z1 = [QNZone createWithHost:@"upload-z1.qiniu.com" backupHost:@"up-z1.qiniu.com" ip1:@"106.38.227.28" ip2:@"106.38.227.27"];
});
return z1;
}
+ (instancetype)zone2 {
static QNZone *z2 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
z2 = [QNZone createWithHost:@"upload-z2.qiniu.com" backupHost:@"up-z2.qiniu.com" ip1:@"14.152.37.7" ip2:@"183.60.214.199"];
});
return z2;
}
+ (instancetype)zoneNa0 {
static QNZone *zNa0 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
zNa0 = [QNZone createWithHost:@"upload-na0.qiniu.com" backupHost:@"up-na0.qiniu.com" ip1:@"14.152.37.7" ip2:@"183.60.214.199"];
});
return zNa0;
}
+ (void)addIpToDns:(QNDnsManager *)dns {
addZoneToDns([QNZone zone0], dns);
addZoneToDns([QNZone zone1], dns);
addZoneToDns([QNZone zone2], dns);
}
- (void)preQuery:(QNUpToken *)token
on:(QNPrequeryReturn)ret {
ret(0);
}
@end

View File

@@ -0,0 +1,26 @@
//
// QNFormUpload.h
// QiniuSDK
//
// Created by bailong on 15/1/4.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import "QNHttpDelegate.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import <Foundation/Foundation.h>
@interface QNFormUpload : NSObject
- (instancetype)initWithData:(NSData *)data
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
- (void)put;
@end

View File

@@ -0,0 +1,133 @@
//
// QNFormUpload.m
// QiniuSDK
//
// Created by bailong on 15/1/4.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNFormUpload.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNRecorderDelegate.h"
#import "QNResponseInfo.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
#import "QNUrlSafeBase64.h"
@interface QNFormUpload ()
@property (nonatomic, strong) NSData *data;
@property (nonatomic, strong) id<QNHttpDelegate> httpManager;
@property (nonatomic) int retryTimes;
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) QNUpToken *token;
@property (nonatomic, strong) QNUploadOption *option;
@property (nonatomic, strong) QNUpCompletionHandler complete;
@property (nonatomic, strong) QNConfiguration *config;
@property (nonatomic) float previousPercent;
@property (nonatomic, strong) NSString *access; //AK
@end
@implementation QNFormUpload
- (instancetype)initWithData:(NSData *)data
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config {
if (self = [super init]) {
_data = data;
_key = key;
_token = token;
_option = option != nil ? option : [QNUploadOption defaultOptions];
_complete = block;
_httpManager = http;
_config = config;
_previousPercent = 0;
_access = token.access;
}
return self;
}
- (void)put {
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
NSString *fileName = _key;
if (_key) {
parameters[@"key"] = _key;
} else {
fileName = @"?";
}
parameters[@"token"] = _token.token;
[parameters addEntriesFromDictionary:_option.params];
if (_option.checkCrc) {
parameters[@"crc32"] = [NSString stringWithFormat:@"%u", (unsigned int)[QNCrc32 data:_data]];
}
QNInternalProgressBlock p = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float percent = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
if (percent > 0.95) {
percent = 0.95;
}
if (percent > _previousPercent) {
_previousPercent = percent;
} else {
percent = _previousPercent;
}
_option.progressHandler(_key, percent);
};
QNCompleteBlock complete = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
_option.progressHandler(_key, 1.0);
}
if (info.isOK || !info.couldRetry) {
_complete(info, _key, resp);
return;
}
if (_option.cancellationSignal()) {
_complete([QNResponseInfo cancel], _key, nil);
return;
}
NSString *nextHost = [_config.zone up:_token].address;
if (info.isConnectionBroken || info.needSwitchServer) {
nextHost = [_config.zone upBackup:_token].address;
}
QNCompleteBlock retriedComplete = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
_option.progressHandler(_key, 1.0);
}
_complete(info, _key, resp);
};
[_httpManager multipartPost:nextHost
withData:_data
withParams:parameters
withFileName:fileName
withMimeType:_option.mimeType
withCompleteBlock:retriedComplete
withProgressBlock:p
withCancelBlock:_option.cancellationSignal
withAccess:_access];
};
[_httpManager multipartPost:[_config.zone up:_token].address
withData:_data
withParams:parameters
withFileName:fileName
withMimeType:_option.mimeType
withCompleteBlock:complete
withProgressBlock:p
withCancelBlock:_option.cancellationSignal
withAccess:_access];
}
@end

View File

@@ -0,0 +1,29 @@
//
// QNResumeUpload.h
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "QNFileDelegate.h"
#import "QNHttpDelegate.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import <Foundation/Foundation.h>
@interface QNResumeUpload : NSObject
- (instancetype)initWithFile:(id<QNFileDelegate>)file
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withRecorder:(id<QNRecorderDelegate>)recorder
withRecorderKey:(NSString *)recorderKey
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
- (void)run;
@end

View File

@@ -0,0 +1,337 @@
//
// QNResumeUpload.m
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNResumeUpload.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNRecorderDelegate.h"
#import "QNResponseInfo.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
#import "QNUrlSafeBase64.h"
typedef void (^task)(void);
@interface QNResumeUpload ()
@property (nonatomic, strong) id<QNHttpDelegate> httpManager;
@property UInt32 size;
@property (nonatomic) int retryTimes;
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) NSString *recorderKey;
@property (nonatomic) NSDictionary *headers;
@property (nonatomic, strong) QNUploadOption *option;
@property (nonatomic, strong) QNUpToken *token;
@property (nonatomic, strong) QNUpCompletionHandler complete;
@property (nonatomic, strong) NSMutableArray *contexts;
@property int64_t modifyTime;
@property (nonatomic, strong) id<QNRecorderDelegate> recorder;
@property (nonatomic, strong) QNConfiguration *config;
@property UInt32 chunkCrc;
@property (nonatomic, strong) id<QNFileDelegate> file;
//@property (nonatomic, strong) NSArray *fileAry;
@property (nonatomic) float previousPercent;
@property (nonatomic, strong) NSString *access; //AK
- (void)makeBlock:(NSString *)uphost
offset:(UInt32)offset
blockSize:(UInt32)blockSize
chunkSize:(UInt32)chunkSize
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete;
- (void)putChunk:(NSString *)uphost
offset:(UInt32)offset
size:(UInt32)size
context:(NSString *)context
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete;
- (void)makeFile:(NSString *)uphost
complete:(QNCompleteBlock)complete;
@end
@implementation QNResumeUpload
- (instancetype)initWithFile:(id<QNFileDelegate>)file
withKey:(NSString *)key
withToken:(QNUpToken *)token
withCompletionHandler:(QNUpCompletionHandler)block
withOption:(QNUploadOption *)option
withRecorder:(id<QNRecorderDelegate>)recorder
withRecorderKey:(NSString *)recorderKey
withHttpManager:(id<QNHttpDelegate>)http
withConfiguration:(QNConfiguration *)config;
{
if (self = [super init]) {
_file = file;
_size = (UInt32)[file size];
_key = key;
NSString *tokenUp = [NSString stringWithFormat:@"UpToken %@", token.token];
_option = option != nil ? option : [QNUploadOption defaultOptions];
_complete = block;
_headers = @{@"Authorization" : tokenUp, @"Content-Type" : @"application/octet-stream"};
_recorder = recorder;
_httpManager = http;
_modifyTime = [file modifyTime];
_recorderKey = recorderKey;
_contexts = [[NSMutableArray alloc] initWithCapacity:(_size + kQNBlockSize - 1) / kQNBlockSize];
_config = config;
_token = token;
_previousPercent = 0;
_access = token.access;
}
return self;
}
// save json value
//{
// "size":filesize,
// "offset":lastSuccessOffset,
// "modify_time": lastFileModifyTime,
// "contexts": contexts
//}
- (void)record:(UInt32)offset {
NSString *key = self.recorderKey;
if (offset == 0 || _recorder == nil || key == nil || [key isEqualToString:@""]) {
return;
}
NSNumber *n_size = @(self.size);
NSNumber *n_offset = @(offset);
NSNumber *n_time = [NSNumber numberWithLongLong:_modifyTime];
NSMutableDictionary *rec = [NSMutableDictionary dictionaryWithObjectsAndKeys:n_size, @"size", n_offset, @"offset", n_time, @"modify_time", _contexts, @"contexts", nil];
NSError *error;
NSData *data = [NSJSONSerialization dataWithJSONObject:rec options:NSJSONWritingPrettyPrinted error:&error];
if (error != nil) {
NSLog(@"up record json error %@ %@", key, error);
return;
}
error = [_recorder set:key data:data];
if (error != nil) {
NSLog(@"up record set error %@ %@", key, error);
}
}
- (void)removeRecord {
if (_recorder == nil) {
return;
}
[_recorder del:self.recorderKey];
}
- (UInt32)recoveryFromRecord {
NSString *key = self.recorderKey;
if (_recorder == nil || key == nil || [key isEqualToString:@""]) {
return 0;
}
NSData *data = [_recorder get:key];
if (data == nil) {
return 0;
}
NSError *error;
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
if (error != nil) {
NSLog(@"recovery error %@ %@", key, error);
[_recorder del:self.key];
return 0;
}
NSNumber *n_offset = info[@"offset"];
NSNumber *n_size = info[@"size"];
NSNumber *time = info[@"modify_time"];
NSArray *contexts = info[@"contexts"];
if (n_offset == nil || n_size == nil || time == nil || contexts == nil) {
return 0;
}
UInt32 offset = [n_offset unsignedIntValue];
UInt32 size = [n_size unsignedIntValue];
if (offset > size || size != self.size) {
return 0;
}
UInt64 t = [time unsignedLongLongValue];
if (t != _modifyTime) {
NSLog(@"modify time changed %llu, %llu", t, _modifyTime);
return 0;
}
_contexts = [[NSMutableArray alloc] initWithArray:contexts copyItems:true];
return offset;
}
- (void)nextTask:(UInt32)offset retriedTimes:(int)retried host:(NSString *)host {
if (self.option.cancellationSignal()) {
self.complete([QNResponseInfo cancel], self.key, nil);
return;
}
if (offset == self.size) {
QNCompleteBlock completionHandler = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.isOK) {
[self removeRecord];
self.option.progressHandler(self.key, 1.0);
} else if (info.couldRetry && retried < _config.retryMax) {
[self nextTask:offset retriedTimes:retried + 1 host:host];
return;
}
self.complete(info, self.key, resp);
};
[self makeFile:host complete:completionHandler];
return;
}
UInt32 chunkSize = [self calcPutSize:offset];
QNInternalProgressBlock progressBlock = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float percent = (float)(offset + totalBytesWritten) / (float)self.size;
if (percent > 0.95) {
percent = 0.95;
}
if (percent > _previousPercent) {
_previousPercent = percent;
} else {
percent = _previousPercent;
}
self.option.progressHandler(self.key, percent);
};
QNCompleteBlock completionHandler = ^(QNResponseInfo *info, NSDictionary *resp) {
if (info.error != nil) {
if (info.statusCode == 701) {
[self nextTask:(offset / kQNBlockSize) * kQNBlockSize retriedTimes:0 host:host];
return;
}
if (retried >= _config.retryMax || !info.couldRetry) {
self.complete(info, self.key, resp);
return;
}
NSString *nextHost = host;
if (info.isConnectionBroken || info.needSwitchServer) {
nextHost = [_config.zone upBackup:_token].address;
}
[self nextTask:offset retriedTimes:retried + 1 host:nextHost];
return;
}
if (resp == nil) {
[self nextTask:offset retriedTimes:retried host:host];
return;
}
NSString *ctx = resp[@"ctx"];
NSNumber *crc = resp[@"crc32"];
if (ctx == nil || crc == nil || [crc unsignedLongValue] != _chunkCrc) {
[self nextTask:offset retriedTimes:retried host:host];
return;
}
_contexts[offset / kQNBlockSize] = ctx;
[self record:offset + chunkSize];
[self nextTask:offset + chunkSize retriedTimes:retried host:host];
};
if (offset % kQNBlockSize == 0) {
UInt32 blockSize = [self calcBlockSize:offset];
[self makeBlock:host offset:offset blockSize:blockSize chunkSize:chunkSize progress:progressBlock complete:completionHandler];
return;
}
NSString *context = _contexts[offset / kQNBlockSize];
[self putChunk:host offset:offset size:chunkSize context:context progress:progressBlock complete:completionHandler];
}
- (UInt32)calcPutSize:(UInt32)offset {
UInt32 left = self.size - offset;
return left < _config.chunkSize ? left : _config.chunkSize;
}
- (UInt32)calcBlockSize:(UInt32)offset {
UInt32 left = self.size - offset;
return left < kQNBlockSize ? left : kQNBlockSize;
}
- (void)makeBlock:(NSString *)uphost
offset:(UInt32)offset
blockSize:(UInt32)blockSize
chunkSize:(UInt32)chunkSize
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete {
NSData *data = [self.file read:offset size:chunkSize];
NSString *url = [[NSString alloc] initWithFormat:@"%@/mkblk/%u", uphost, (unsigned int)blockSize];
_chunkCrc = [QNCrc32 data:data];
[self post:url withData:data withCompleteBlock:complete withProgressBlock:progressBlock];
}
- (void)putChunk:(NSString *)uphost
offset:(UInt32)offset
size:(UInt32)size
context:(NSString *)context
progress:(QNInternalProgressBlock)progressBlock
complete:(QNCompleteBlock)complete {
NSData *data = [self.file read:offset size:size];
UInt32 chunkOffset = offset % kQNBlockSize;
NSString *url = [[NSString alloc] initWithFormat:@"%@/bput/%@/%u", uphost, context, (unsigned int)chunkOffset];
_chunkCrc = [QNCrc32 data:data];
[self post:url withData:data withCompleteBlock:complete withProgressBlock:progressBlock];
}
- (void)makeFile:(NSString *)uphost
complete:(QNCompleteBlock)complete {
NSString *mime = [[NSString alloc] initWithFormat:@"/mimeType/%@", [QNUrlSafeBase64 encodeString:self.option.mimeType]];
__block NSString *url = [[NSString alloc] initWithFormat:@"%@/mkfile/%u%@", uphost, (unsigned int)self.size, mime];
if (self.key != nil) {
NSString *keyStr = [[NSString alloc] initWithFormat:@"/key/%@", [QNUrlSafeBase64 encodeString:self.key]];
url = [NSString stringWithFormat:@"%@%@", url, keyStr];
}
[self.option.params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
url = [NSString stringWithFormat:@"%@/%@/%@", url, key, [QNUrlSafeBase64 encodeString:obj]];
}];
//
NSString *fname = [[NSString alloc] initWithFormat:@"/fname/%@", [QNUrlSafeBase64 encodeString:[self fileBaseName]]];
url = [NSString stringWithFormat:@"%@%@", url, fname];
NSMutableData *postData = [NSMutableData data];
NSString *bodyStr = [self.contexts componentsJoinedByString:@","];
[postData appendData:[bodyStr dataUsingEncoding:NSUTF8StringEncoding]];
[self post:url withData:postData withCompleteBlock:complete withProgressBlock:nil];
}
#pragma mark -
- (NSString *)fileBaseName {
return [[_file path] lastPathComponent];
}
- (void)post:(NSString *)url
withData:(NSData *)data
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock {
[_httpManager post:url withData:data withParams:nil withHeaders:_headers withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:_option.cancellationSignal withAccess:_access];
}
- (void)run {
@autoreleasepool {
UInt32 offset = [self recoveryFromRecord];
[self nextTask:offset retriedTimes:0 host:[_config.zone up:_token].address];
}
}
@end

View File

@@ -0,0 +1,23 @@
//
// QNUpToken.h
// QiniuSDK
//
// Created by bailong on 15/6/7.
// Copyright (c) 2015年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNUpToken : NSObject
+ (instancetype)parse:(NSString *)token;
@property (copy, nonatomic, readonly) NSString *access;
@property (copy, nonatomic, readonly) NSString *bucket;
@property (copy, nonatomic, readonly) NSString *token;
@property (readonly) BOOL hasReturnUrl;
- (NSString *)index;
@end

View File

@@ -0,0 +1,73 @@
//
// QNUpToken.m
// QiniuSDK
//
// Created by bailong on 15/6/7.
// Copyright (c) 2015 Qiniu. All rights reserved.
//
#import "QNUrlSafeBase64.h"
#import "QNUpToken.h"
@interface QNUpToken ()
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token;
@end
@implementation QNUpToken
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token {
if (self = [super init]) {
_token = token;
_access = [self getAccess];
_bucket = [self getBucket:policy];
_hasReturnUrl = (policy[@"returnUrl"] != nil);
}
return self;
}
- (NSString *)getAccess {
NSRange range = [_token rangeOfString:@":" options:NSCaseInsensitiveSearch];
return [_token substringToIndex:range.location];
}
- (NSString *)getBucket:(NSDictionary *)info {
NSString *scope = [info objectForKey:@"scope"];
if (!scope) {
return @"";
}
NSRange range = [scope rangeOfString:@":"];
if (range.location == NSNotFound) {
return scope;
}
return [scope substringToIndex:range.location];
}
+ (instancetype)parse:(NSString *)token {
if (token == nil) {
return nil;
}
NSArray *array = [token componentsSeparatedByString:@":"];
if (array == nil || array.count != 3) {
return nil;
}
NSData *data = [QNUrlSafeBase64 decodeString:array[2]];
NSError *tmp = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
if (tmp != nil || dict[@"scope"] == nil || dict[@"deadline"] == nil) {
return nil;
}
return [[QNUpToken alloc] init:dict token:token];
}
- (NSString *)index {
return [NSString stringWithFormat:@"%@:%@", _access, _bucket];
}
@end

View File

@@ -0,0 +1,155 @@
//
// QNUploader.h
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNRecorderDelegate.h"
@class QNResponseInfo;
@class QNUploadOption;
@class QNConfiguration;
@class ALAsset;
@class PHAsset;
@class PHAssetResource;
/**
* 上传完成后的回调函数
*
* @param info 上下文信息,包括状态码,错误值
* @param key 上传时指定的key原样返回
* @param resp 上传成功会返回文件信息失败为nil; 可以通过此值是否为nil 判断上传结果
*/
typedef void (^QNUpCompletionHandler)(QNResponseInfo *info, NSString *key, NSDictionary *resp);
/**
管理上传的类,可以生成一次,持续使用,不必反复创建。
*/
@interface QNUploadManager : NSObject
/**
* 默认构造方法,没有持久化记录
*
* @return 上传管理类实例
*/
- (instancetype)init;
/**
* 使用一个持久化的记录接口进行记录的构造方法
*
* @param recorder 持久化记录接口实现
*
* @return 上传管理类实例
*/
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder;
/**
* 使用持久化记录接口以及持久化key生成函数的构造方法默认情况下使用上传存储的key, 如果key为nil或者有特殊字符比如/,建议使用自己的生成函数
*
* @param recorder 持久化记录接口实现
* @param recorderKeyGenerator 持久化记录key生成函数
*
* @return 上传管理类实例
*/
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder
recorderKeyGenerator:(QNRecorderKeyGenerator)recorderKeyGenerator;
/**
* 使用配置信息生成上传实例
*
* @param config 配置信息
*
* @return 上传管理类实例
*/
- (instancetype)initWithConfiguration:(QNConfiguration *)config;
/**
* 方便使用的单例方法
*
* @param config 配置信息
*
* @return 上传管理类实例
*/
+ (instancetype)sharedInstanceWithConfiguration:(QNConfiguration *)config;
/**
* 直接上传数据
*
* @param data 待上传的数据
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putData:(NSData *)data
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传文件
*
* @param filePath 文件路径
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putFile:(NSString *)filePath
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传ALAsset文件
*
* @param alasset ALAsset文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putALAsset:(ALAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传PHAsset文件(IOS8 andLater)
*
* @param asset PHAsset文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putPHAsset:(PHAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
/**
* 上传PHAssetResource文件(IOS9.1 andLater)
*
* @param asset PHAssetResource文件
* @param key 上传到云存储的key为nil时表示是由七牛生成
* @param token 上传需要的token, 由服务器生成
* @param completionHandler 上传完成后的回调函数
* @param option 上传时传入的可选参数
*/
- (void)putPHAssetResource:(PHAssetResource *)assetResource
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option;
@end

View File

@@ -0,0 +1,321 @@
//
// QNUploader.h
// QiniuSDK
//
// Created by bailong on 14-9-28.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import "QNALAssetFile.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
#import "QNPHAssetFile.h"
#import <Photos/Photos.h>
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100
#import "QNPHAssetResource.h"
#endif
#else
#import <CoreServices/CoreServices.h>
#endif
#import "QNAsyncRun.h"
#import "QNConfiguration.h"
#import "QNCrc32.h"
#import "QNFile.h"
#import "QNFormUpload.h"
#import "QNResponseInfo.h"
#import "QNResumeUpload.h"
#import "QNSessionManager.h"
#import "QNSystem.h"
#import "QNUpToken.h"
#import "QNUploadManager.h"
#import "QNUploadOption+Private.h"
@interface QNUploadManager ()
@property (nonatomic) id<QNHttpDelegate> httpManager;
@property (nonatomic) QNConfiguration *config;
@end
@implementation QNUploadManager
- (instancetype)init {
return [self initWithConfiguration:nil];
}
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder {
return [self initWithRecorder:recorder recorderKeyGenerator:nil];
}
- (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder
recorderKeyGenerator:(QNRecorderKeyGenerator)recorderKeyGenerator {
QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
builder.recorder = recorder;
builder.recorderKeyGen = recorderKeyGenerator;
}];
return [self initWithConfiguration:config];
}
- (instancetype)initWithConfiguration:(QNConfiguration *)config {
if (self = [super init]) {
if (config == nil) {
config = [QNConfiguration build:^(QNConfigurationBuilder *builder){
}];
}
_config = config;
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
_httpManager = [[QNSessionManager alloc] initWithProxy:config.proxy timeout:config.timeoutInterval urlConverter:config.converter dns:config.dns];
#endif
}
return self;
}
+ (instancetype)sharedInstanceWithConfiguration:(QNConfiguration *)config {
static QNUploadManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initWithConfiguration:config];
});
return sharedInstance;
}
+ (BOOL)checkAndNotifyError:(NSString *)key
token:(NSString *)token
input:(NSObject *)input
complete:(QNUpCompletionHandler)completionHandler {
NSString *desc = nil;
if (completionHandler == nil) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"no completionHandler"
userInfo:nil];
return YES;
}
if (input == nil) {
desc = @"no input data";
} else if (token == nil || [token isEqualToString:@""]) {
desc = @"no token";
}
if (desc != nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidArgument:desc], key, nil);
});
return YES;
}
return NO;
}
- (void)putData:(NSData *)data
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
if ([QNUploadManager checkAndNotifyError:key token:token input:data complete:completionHandler]) {
return;
}
QNUpToken *t = [QNUpToken parse:token];
if (t == nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], key, nil);
});
return;
}
[_config.zone preQuery:t on:^(int code) {
if (code != 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"get zone failed"], key, nil);
});
return;
}
if ([data length] == 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoOfZeroData:nil], key, nil);
});
return;
}
QNUpCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
QNAsyncRunInMain(^{
completionHandler(info, key, resp);
});
};
QNFormUpload *up = [[QNFormUpload alloc]
initWithData:data
withKey:key
withToken:t
withCompletionHandler:complete
withOption:option
withHttpManager:_httpManager
withConfiguration:_config];
QNAsyncRun(^{
[up put];
});
}];
}
- (void)putFileInternal:(id<QNFileDelegate>)file
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
@autoreleasepool {
QNUpToken *t = [QNUpToken parse:token];
if (t == nil) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], key, nil);
});
return;
}
[_config.zone preQuery:t on:^(int code) {
if (code != 0) {
QNAsyncRunInMain(^{
completionHandler([QNResponseInfo responseInfoWithInvalidToken:@"get zone failed"], key, nil);
});
return;
}
QNUpCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
[file close];
QNAsyncRunInMain(^{
completionHandler(info, key, resp);
});
};
if ([file size] <= _config.putThreshold) {
NSData *data = [file readAll];
[self putData:data key:key token:token complete:complete option:option];
return;
}
NSString *recorderKey = key;
if (_config.recorder != nil && _config.recorderKeyGen != nil) {
recorderKey = _config.recorderKeyGen(key, [file path]);
}
NSLog(@"recorder %@", _config.recorder);
QNResumeUpload *up = [[QNResumeUpload alloc]
initWithFile:file
withKey:key
withToken:t
withCompletionHandler:complete
withOption:option
withRecorder:_config.recorder
withRecorderKey:recorderKey
withHttpManager:_httpManager
withConfiguration:_config];
QNAsyncRun(^{
[up run];
});
}];
}
}
- (void)putFile:(NSString *)filePath
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
if ([QNUploadManager checkAndNotifyError:key token:token input:filePath complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNFile *file = [[QNFile alloc] init:filePath error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
}
- (void)putALAsset:(ALAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNALAssetFile *file = [[QNALAssetFile alloc] init:asset error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
- (void)putPHAsset:(PHAsset *)asset
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNPHAssetFile *file = [[QNPHAssetFile alloc] init:asset error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
- (void)putPHAssetResource:(PHAssetResource *)assetResource
key:(NSString *)key
token:(NSString *)token
complete:(QNUpCompletionHandler)completionHandler
option:(QNUploadOption *)option {
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100)
if ([QNUploadManager checkAndNotifyError:key token:token input:assetResource complete:completionHandler]) {
return;
}
@autoreleasepool {
NSError *error = nil;
__block QNPHAssetResource *file = [[QNPHAssetResource alloc] init:assetResource error:&error];
if (error) {
QNAsyncRunInMain(^{
QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
completionHandler(info, key, nil);
});
return;
}
[self putFileInternal:file key:key token:token complete:completionHandler option:option];
}
#endif
}
@end

View File

@@ -0,0 +1,14 @@
//
// QNUploadOption+Private.h
// QiniuSDK
//
// Created by bailong on 14/10/5.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "QNUploadOption.h"
@interface QNUploadOption (Private)
@property (nonatomic, getter=priv_isCancelled, readonly) BOOL cancelled;
@end

View File

@@ -0,0 +1,83 @@
//
// QNUploadOption.h
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* 上传进度回调函数
*
* @param key 上传时指定的存储key
* @param percent 进度百分比
*/
typedef void (^QNUpProgressHandler)(NSString *key, float percent);
/**
* 上传中途取消函数
*
* @return 如果想取消返回True, 否则返回No
*/
typedef BOOL (^QNUpCancellationSignal)(void);
/**
* 可选参数集合此类初始化后sdk上传使用时 不会对此进行改变;如果参数没有变化以及没有使用依赖,可以重复使用。
*/
@interface QNUploadOption : NSObject
/**
* 用于服务器上传回调通知的自定义参数参数的key必须以x: 开头
*/
@property (copy, nonatomic, readonly) NSDictionary *params;
/**
* 指定文件的mime类型
*/
@property (copy, nonatomic, readonly) NSString *mimeType;
/**
* 是否进行crc校验
*/
@property (readonly) BOOL checkCrc;
/**
* 进度回调函数
*/
@property (copy, readonly) QNUpProgressHandler progressHandler;
/**
* 中途取消函数
*/
@property (copy, readonly) QNUpCancellationSignal cancellationSignal;
/**
* 可选参数的初始化方法
*
* @param mimeType mime类型
* @param progress 进度函数
* @param params 自定义服务器回调参数
* @param check 是否进行crc检查
* @param cancellation 中途取消函数
*
* @return 可选参数类实例
*/
- (instancetype)initWithMime:(NSString *)mimeType
progressHandler:(QNUpProgressHandler)progress
params:(NSDictionary *)params
checkCrc:(BOOL)check
cancellationSignal:(QNUpCancellationSignal)cancellation;
- (instancetype)initWithProgessHandler:(QNUpProgressHandler)progress DEPRECATED_ATTRIBUTE;
- (instancetype)initWithProgressHandler:(QNUpProgressHandler)progress;
/**
* 内部使用,默认的参数实例
*
* @return 可选参数类实例
*/
+ (instancetype)defaultOptions;
@end

View File

@@ -0,0 +1,67 @@
//
// QNUploadOption.m
// QiniuSDK
//
// Created by bailong on 14/10/4.
// Copyright (c) 2014 Qiniu. All rights reserved.
//
#import "QNUploadOption+Private.h"
#import "QNUploadManager.h"
static NSString *mime(NSString *mimeType) {
if (mimeType == nil || [mimeType isEqualToString:@""]) {
return @"application/octet-stream";
}
return mimeType;
}
@implementation QNUploadOption
+ (NSDictionary *)filteParam:(NSDictionary *)params {
NSMutableDictionary *ret = [NSMutableDictionary dictionary];
if (params == nil) {
return ret;
}
[params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
if ([key hasPrefix:@"x:"] && ![obj isEqualToString:@""]) {
ret[key] = obj;
}
}];
return ret;
}
- (instancetype)initWithProgessHandler:(QNUpProgressHandler)progress {
return [self initWithMime:nil progressHandler:progress params:nil checkCrc:NO cancellationSignal:nil];
}
- (instancetype)initWithProgressHandler:(QNUpProgressHandler)progress {
return [self initWithMime:nil progressHandler:progress params:nil checkCrc:NO cancellationSignal:nil];
}
- (instancetype)initWithMime:(NSString *)mimeType
progressHandler:(QNUpProgressHandler)progress
params:(NSDictionary *)params
checkCrc:(BOOL)check
cancellationSignal:(QNUpCancellationSignal)cancel {
if (self = [super init]) {
_mimeType = mime(mimeType);
_progressHandler = progress != nil ? progress : ^(NSString *key, float percent) {
};
_params = [QNUploadOption filteParam:params];
_checkCrc = check;
_cancellationSignal = cancel != nil ? cancel : ^BOOL() {
return NO;
};
}
return self;
}
+ (instancetype)defaultOptions {
return [[QNUploadOption alloc] initWithMime:nil progressHandler:nil params:nil checkCrc:NO cancellationSignal:nil];
}
@end