QNBaseUpload.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //
  2. // QNBaseUpload.m
  3. // QiniuSDK
  4. //
  5. // Created by WorkSpace_Sun on 2020/4/19.
  6. // Copyright © 2020 Qiniu. All rights reserved.
  7. //
  8. #import "QNZoneInfo.h"
  9. #import "QNResponseInfo.h"
  10. #import "QNDefine.h"
  11. #import "QNBaseUpload.h"
  12. #import "QNUploadDomainRegion.h"
  13. NSString *const QNUploadUpTypeForm = @"form";
  14. NSString *const QNUploadUpTypeResumableV1 = @"resumable_v1";
  15. NSString *const QNUploadUpTypeResumableV2 = @"resumable_v2";
  16. @interface QNBaseUpload ()
  17. @property (nonatomic, strong) QNBaseUpload *strongSelf;
  18. @property (nonatomic, copy) NSString *key;
  19. @property (nonatomic, copy) NSString *fileName;
  20. @property (nonatomic, strong) NSData *data;
  21. @property (nonatomic, strong) id <QNUploadSource> uploadSource;
  22. @property (nonatomic, strong) QNUpToken *token;
  23. @property (nonatomic, copy) NSString *identifier;
  24. @property (nonatomic, strong) QNUploadOption *option;
  25. @property (nonatomic, strong) QNConfiguration *config;
  26. @property (nonatomic, strong) id <QNRecorderDelegate> recorder;
  27. @property (nonatomic, copy) NSString *recorderKey;
  28. @property (nonatomic, strong) QNUpTaskCompletionHandler completionHandler;
  29. @property (nonatomic, assign)NSInteger currentRegionIndex;
  30. @property (nonatomic, strong)NSMutableArray <id <QNUploadRegion> > *regions;
  31. @property (nonatomic, strong)QNUploadRegionRequestMetrics *currentRegionRequestMetrics;
  32. @property (nonatomic, strong) QNUploadTaskMetrics *metrics;
  33. @end
  34. @implementation QNBaseUpload
  35. - (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
  36. key:(NSString *)key
  37. token:(QNUpToken *)token
  38. option:(QNUploadOption *)option
  39. configuration:(QNConfiguration *)config
  40. recorder:(id<QNRecorderDelegate>)recorder
  41. recorderKey:(NSString *)recorderKey
  42. completionHandler:(QNUpTaskCompletionHandler)completionHandler{
  43. return [self initWithSource:uploadSource data:nil fileName:[uploadSource getFileName] key:key token:token option:option configuration:config recorder:recorder recorderKey:recorderKey completionHandler:completionHandler];
  44. }
  45. - (instancetype)initWithData:(NSData *)data
  46. key:(NSString *)key
  47. fileName:(NSString *)fileName
  48. token:(QNUpToken *)token
  49. option:(QNUploadOption *)option
  50. configuration:(QNConfiguration *)config
  51. completionHandler:(QNUpTaskCompletionHandler)completionHandler{
  52. return [self initWithSource:nil data:data fileName:fileName key:key token:token option:option configuration:config recorder:nil recorderKey:nil completionHandler:completionHandler];
  53. }
  54. - (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
  55. data:(NSData *)data
  56. fileName:(NSString *)fileName
  57. key:(NSString *)key
  58. token:(QNUpToken *)token
  59. option:(QNUploadOption *)option
  60. configuration:(QNConfiguration *)config
  61. recorder:(id<QNRecorderDelegate>)recorder
  62. recorderKey:(NSString *)recorderKey
  63. completionHandler:(QNUpTaskCompletionHandler)completionHandler{
  64. if (self = [super init]) {
  65. _uploadSource = uploadSource;
  66. _data = data;
  67. _fileName = fileName ?: @"?";
  68. _key = key;
  69. _token = token;
  70. _config = config;
  71. _option = option ?: [QNUploadOption defaultOptions];
  72. _recorder = recorder;
  73. _recorderKey = recorderKey;
  74. _completionHandler = completionHandler;
  75. [self initData];
  76. }
  77. return self;
  78. }
  79. - (instancetype)init{
  80. if (self = [super init]) {
  81. [self initData];
  82. }
  83. return self;
  84. }
  85. - (void)initData{
  86. _strongSelf = self;
  87. _currentRegionIndex = 0;
  88. }
  89. - (void)run {
  90. [self.metrics start];
  91. kQNWeakSelf;
  92. [_config.zone preQuery:self.token on:^(int code, QNResponseInfo *responseInfo, QNUploadRegionRequestMetrics *metrics) {
  93. kQNStrongSelf;
  94. self.metrics.ucQueryMetrics = metrics;
  95. if (code == 0) {
  96. int prepareCode = [self prepareToUpload];
  97. if (prepareCode == 0) {
  98. [self startToUpload];
  99. } else {
  100. QNResponseInfo *responseInfoP = [QNResponseInfo errorResponseInfo:prepareCode errorDesc:nil];
  101. [self complete:responseInfoP response:responseInfoP.responseDictionary];
  102. }
  103. } else {
  104. [self complete:responseInfo response:responseInfo.responseDictionary];
  105. }
  106. }];
  107. }
  108. - (BOOL)reloadUploadInfo {
  109. return YES;
  110. }
  111. - (int)prepareToUpload{
  112. int ret = 0;
  113. if (![self setupRegions]) {
  114. ret = -1;
  115. }
  116. return ret;
  117. }
  118. - (void)startToUpload{
  119. self.currentRegionRequestMetrics = [[QNUploadRegionRequestMetrics alloc] initWithRegion:[self getCurrentRegion]];
  120. [self.currentRegionRequestMetrics start];
  121. }
  122. // 内部不再调用
  123. - (BOOL)switchRegionAndUpload{
  124. if (self.currentRegionRequestMetrics) {
  125. [self.currentRegionRequestMetrics end];
  126. [self.metrics addMetrics:self.currentRegionRequestMetrics];
  127. self.currentRegionRequestMetrics = nil;
  128. }
  129. BOOL isSwitched = [self switchRegion];
  130. if (isSwitched) {
  131. [self startToUpload];
  132. }
  133. return isSwitched;
  134. }
  135. // 根据错误信息进行切换region并上传,return:是否切换region并上传
  136. - (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo {
  137. if (!errorResponseInfo || errorResponseInfo.isOK || // 不存在 || 成功 不需要重试
  138. ![errorResponseInfo couldRetry] || ![self.config allowBackupHost]) { // 不能重试
  139. return false;
  140. }
  141. if (self.currentRegionRequestMetrics) {
  142. [self.currentRegionRequestMetrics end];
  143. [self.metrics addMetrics:self.currentRegionRequestMetrics];
  144. self.currentRegionRequestMetrics = nil;
  145. }
  146. // 重新加载上传数据,上传记录 & Resource index 归零
  147. if (![self reloadUploadInfo]) {
  148. return false;
  149. }
  150. // 切换区域,当为 context 过期错误不需要切换区域
  151. if (!errorResponseInfo.isCtxExpiedError && ![self switchRegion]) {
  152. // 非 context 过期错误,但是切换 region 失败
  153. return false;
  154. }
  155. [self startToUpload];
  156. return true;
  157. }
  158. - (void)complete:(QNResponseInfo *)info
  159. response:(NSDictionary *)response{
  160. [self.metrics end];
  161. [self.currentRegionRequestMetrics end];
  162. if (self.currentRegionRequestMetrics) {
  163. [self.metrics addMetrics:self.currentRegionRequestMetrics];
  164. }
  165. if (self.completionHandler) {
  166. self.completionHandler(info, _key, _metrics, response);
  167. }
  168. self.strongSelf = nil;
  169. }
  170. //MARK:-- region
  171. - (BOOL)setupRegions{
  172. NSMutableArray *defaultRegions = [NSMutableArray array];
  173. NSArray *zoneInfos = [self.config.zone getZonesInfoWithToken:self.token].zonesInfo;
  174. for (QNZoneInfo *zoneInfo in zoneInfos) {
  175. QNUploadDomainRegion *region = [[QNUploadDomainRegion alloc] init];
  176. [region setupRegionData:zoneInfo];
  177. if (region.isValid) {
  178. [defaultRegions addObject:region];
  179. }
  180. }
  181. self.regions = defaultRegions;
  182. self.metrics.regions = defaultRegions;
  183. return defaultRegions.count > 0;
  184. }
  185. - (void)insertRegionAtFirst:(id <QNUploadRegion>)region{
  186. BOOL hasRegion = NO;
  187. for (id <QNUploadRegion> regionP in self.regions) {
  188. if ([regionP.zoneInfo.regionId isEqualToString:region.zoneInfo.regionId]) {
  189. hasRegion = YES;
  190. break;
  191. }
  192. }
  193. if (!hasRegion) {
  194. [self.regions insertObject:region atIndex:0];
  195. }
  196. }
  197. - (BOOL)switchRegion{
  198. BOOL ret = NO;
  199. @synchronized (self) {
  200. NSInteger regionIndex = _currentRegionIndex + 1;
  201. if (regionIndex < self.regions.count) {
  202. _currentRegionIndex = regionIndex;
  203. ret = YES;
  204. }
  205. }
  206. return ret;
  207. }
  208. - (id <QNUploadRegion>)getTargetRegion{
  209. return self.regions.firstObject;
  210. }
  211. - (id <QNUploadRegion>)getCurrentRegion{
  212. id <QNUploadRegion> region = nil;
  213. @synchronized (self) {
  214. if (self.currentRegionIndex < self.regions.count) {
  215. region = self.regions[self.currentRegionIndex];
  216. }
  217. }
  218. return region;
  219. }
  220. - (void)addRegionRequestMetricsOfOneFlow:(QNUploadRegionRequestMetrics *)metrics{
  221. if (metrics == nil) {
  222. return;
  223. }
  224. @synchronized (self) {
  225. if (self.currentRegionRequestMetrics == nil) {
  226. self.currentRegionRequestMetrics = metrics;
  227. return;
  228. }
  229. }
  230. [self.currentRegionRequestMetrics addMetrics:metrics];
  231. }
  232. - (QNUploadTaskMetrics *)metrics {
  233. if (_metrics == nil) {
  234. _metrics = [QNUploadTaskMetrics taskMetrics:self.upType];
  235. }
  236. return _metrics;
  237. }
  238. @end