QNPartsUpload.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //
  2. // QNPartsUpload.m
  3. // QiniuSDK_Mac
  4. //
  5. // Created by yangsen on 2020/5/7.
  6. // Copyright © 2020 Qiniu. All rights reserved.
  7. //
  8. #import "QNDefine.h"
  9. #import "QNUtils.h"
  10. #import "QNLogUtil.h"
  11. #import "QNPartsUpload.h"
  12. #import "QNZoneInfo.h"
  13. #import "QNReportItem.h"
  14. #import "QNRequestTransaction.h"
  15. #import "QNPartsUploadPerformerV1.h"
  16. #import "QNPartsUploadPerformerV2.h"
  17. #define kQNRecordFileInfoKey @"recordFileInfo"
  18. #define kQNRecordZoneInfoKey @"recordZoneInfo"
  19. @interface QNPartsUpload()
  20. @property(nonatomic, strong)QNPartsUploadPerformer *uploadPerformer;
  21. @property(nonatomic, strong)QNResponseInfo *uploadDataErrorResponseInfo;
  22. @property(nonatomic, strong)NSDictionary *uploadDataErrorResponse;
  23. @end
  24. @implementation QNPartsUpload
  25. - (void)initData {
  26. [super initData];
  27. // 根据文件从本地恢复上传信息,如果没有则重新构建上传信息
  28. if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
  29. QNLogInfo(@"key:%@ 分片V1", self.key);
  30. self.uploadPerformer = [[QNPartsUploadPerformerV1 alloc] initWithSource:self.uploadSource
  31. fileName:self.fileName
  32. key:self.key
  33. token:self.token
  34. option:self.option
  35. configuration:self.config
  36. recorderKey:self.recorderKey];
  37. } else {
  38. QNLogInfo(@"key:%@ 分片V2", self.key);
  39. self.uploadPerformer = [[QNPartsUploadPerformerV2 alloc] initWithSource:self.uploadSource
  40. fileName:self.fileName
  41. key:self.key
  42. token:self.token
  43. option:self.option
  44. configuration:self.config
  45. recorderKey:self.recorderKey];
  46. }
  47. }
  48. - (BOOL)isAllUploaded {
  49. return [self.uploadPerformer.uploadInfo isAllUploaded];
  50. }
  51. - (void)setErrorResponseInfo:(QNResponseInfo *)responseInfo errorResponse:(NSDictionary *)response{
  52. if (!responseInfo) {
  53. return;
  54. }
  55. if (!self.uploadDataErrorResponseInfo || responseInfo.statusCode != kQNSDKInteriorError) {
  56. self.uploadDataErrorResponseInfo = responseInfo;
  57. self.uploadDataErrorResponse = response ?: responseInfo.responseDictionary;
  58. }
  59. }
  60. - (int)prepareToUpload{
  61. int code = [super prepareToUpload];
  62. if (code != 0) {
  63. return code;
  64. }
  65. // 配置当前region
  66. if (self.uploadPerformer.currentRegion && self.uploadPerformer.currentRegion.isValid) {
  67. // currentRegion有值,为断点续传,将region插入至regionList第一处
  68. [self insertRegionAtFirst:self.uploadPerformer.currentRegion];
  69. QNLogInfo(@"key:%@ 使用缓存region", self.key);
  70. } else {
  71. // currentRegion无值 切换region
  72. [self.uploadPerformer switchRegion:[self getCurrentRegion]];
  73. }
  74. QNLogInfo(@"key:%@ region:%@", self.key, self.uploadPerformer.currentRegion.zoneInfo.regionId);
  75. if (self.uploadSource == nil) {
  76. code = kQNLocalIOError;
  77. }
  78. return code;
  79. }
  80. - (BOOL)switchRegion{
  81. BOOL isSuccess = [super switchRegion];
  82. if (isSuccess) {
  83. [self.uploadPerformer switchRegion:self.getCurrentRegion];
  84. QNLogInfo(@"key:%@ 切换region:%@", self.key , self.uploadPerformer.currentRegion.zoneInfo.regionId);
  85. }
  86. return isSuccess;
  87. }
  88. - (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo {
  89. [self reportBlock];
  90. return [super switchRegionAndUploadIfNeededWithErrorResponse:errorResponseInfo];
  91. }
  92. - (BOOL)reloadUploadInfo {
  93. BOOL success = [super reloadUploadInfo];
  94. if (![super reloadUploadInfo]) {
  95. return NO;
  96. }
  97. // 重新加载资源
  98. return [self.uploadPerformer couldReloadInfo] && [self.uploadPerformer reloadInfo];
  99. }
  100. - (void)startToUpload{
  101. [super startToUpload];
  102. // 重置错误信息
  103. self.uploadDataErrorResponseInfo = nil;
  104. self.uploadDataErrorResponse = nil;
  105. QNLogInfo(@"key:%@ serverInit", self.key);
  106. // 1. 启动upload
  107. kQNWeakSelf;
  108. [self serverInit:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
  109. kQNStrongSelf;
  110. if (!responseInfo.isOK) {
  111. if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) {
  112. [self complete:responseInfo response:response];
  113. }
  114. return;
  115. }
  116. QNLogInfo(@"key:%@ uploadRestData", self.key);
  117. // 2. 上传数据
  118. kQNWeakSelf;
  119. [self uploadRestData:^{
  120. kQNStrongSelf;
  121. if (![self isAllUploaded]) {
  122. if (![self switchRegionAndUploadIfNeededWithErrorResponse:self.uploadDataErrorResponseInfo]) {
  123. [self complete:self.uploadDataErrorResponseInfo response:self.uploadDataErrorResponse];
  124. }
  125. return;
  126. }
  127. // 只有再读取结束再能知道文件大小,需要检测
  128. if ([self.uploadPerformer.uploadInfo getSourceSize] == 0) {
  129. QNResponseInfo *responseInfo = [QNResponseInfo responseInfoOfZeroData:@"file is empty"];
  130. [self complete:responseInfo response:responseInfo.responseDictionary];
  131. return;
  132. }
  133. QNLogInfo(@"key:%@ completeUpload errorResponseInfo:%@", self.key, self.uploadDataErrorResponseInfo);
  134. // 3. 组装文件
  135. kQNWeakSelf;
  136. [self completeUpload:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
  137. kQNStrongSelf;
  138. if (!responseInfo.isOK) {
  139. if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) {
  140. [self complete:responseInfo response:response];
  141. }
  142. return;
  143. }
  144. [self complete:responseInfo response:response];
  145. }];
  146. }];
  147. }];
  148. }
  149. - (void)uploadRestData:(dispatch_block_t)completeHandler {
  150. QNLogInfo(@"key:%@ 串行分片", self.key);
  151. [self performUploadRestData:completeHandler];
  152. }
  153. - (void)performUploadRestData:(dispatch_block_t)completeHandler {
  154. if ([self isAllUploaded]) {
  155. completeHandler();
  156. return;
  157. }
  158. kQNWeakSelf;
  159. [self uploadNextData:^(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
  160. kQNStrongSelf;
  161. if (stop || !responseInfo.isOK) {
  162. completeHandler();
  163. } else {
  164. [self performUploadRestData:completeHandler];
  165. }
  166. }];
  167. }
  168. //MARK:-- concurrent upload model API
  169. - (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
  170. kQNWeakSelf;
  171. void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
  172. kQNStrongSelf;
  173. if (!responseInfo.isOK) {
  174. [self setErrorResponseInfo:responseInfo errorResponse:response];
  175. }
  176. [self addRegionRequestMetricsOfOneFlow:metrics];
  177. completeHandler(responseInfo, response);
  178. };
  179. [self.uploadPerformer serverInit:completeHandlerP];
  180. }
  181. - (void)uploadNextData:(void(^)(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
  182. kQNWeakSelf;
  183. void(^completeHandlerP)(BOOL, QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(BOOL stop, QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
  184. kQNStrongSelf;
  185. if (!responseInfo.isOK) {
  186. [self setErrorResponseInfo:responseInfo errorResponse:response];
  187. }
  188. [self addRegionRequestMetricsOfOneFlow:metrics];
  189. completeHandler(stop, responseInfo, response);
  190. };
  191. [self.uploadPerformer uploadNextData:completeHandlerP];
  192. }
  193. - (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
  194. kQNWeakSelf;
  195. void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
  196. kQNStrongSelf;
  197. if (!responseInfo.isOK) {
  198. [self setErrorResponseInfo:responseInfo errorResponse:response];
  199. }
  200. [self addRegionRequestMetricsOfOneFlow:metrics];
  201. completeHandler(responseInfo, response);
  202. };
  203. [self.uploadPerformer completeUpload:completeHandlerP];
  204. }
  205. - (void)complete:(QNResponseInfo *)info response:(NSDictionary *)response{
  206. [self.uploadSource close];
  207. if ([self shouldRemoveUploadInfoRecord:info]) {
  208. [self.uploadPerformer removeUploadInfoRecord];
  209. }
  210. [super complete:info response:response];
  211. [self reportBlock];
  212. }
  213. - (BOOL)shouldRemoveUploadInfoRecord:(QNResponseInfo *)info {
  214. return info.isOK || info.statusCode == 612 || info.statusCode == 614 || info.statusCode == 701;
  215. }
  216. //MARK:-- 统计block日志
  217. - (void)reportBlock{
  218. QNUploadRegionRequestMetrics *metrics = self.currentRegionRequestMetrics ?: [QNUploadRegionRequestMetrics emptyMetrics];
  219. QNReportItem *item = [QNReportItem item];
  220. [item setReportValue:QNReportLogTypeBlock forKey:QNReportBlockKeyLogType];
  221. [item setReportValue:@([[NSDate date] timeIntervalSince1970]) forKey:QNReportBlockKeyUpTime];
  222. [item setReportValue:self.token.bucket forKey:QNReportBlockKeyTargetBucket];
  223. [item setReportValue:self.key forKey:QNReportBlockKeyTargetKey];
  224. [item setReportValue:[self getTargetRegion].zoneInfo.regionId forKey:QNReportBlockKeyTargetRegionId];
  225. [item setReportValue:[self getCurrentRegion].zoneInfo.regionId forKey:QNReportBlockKeyCurrentRegionId];
  226. [item setReportValue:metrics.totalElapsedTime forKey:QNReportBlockKeyTotalElapsedTime];
  227. [item setReportValue:metrics.bytesSend forKey:QNReportBlockKeyBytesSent];
  228. [item setReportValue:self.uploadPerformer.recoveredFrom forKey:QNReportBlockKeyRecoveredFrom];
  229. [item setReportValue:@([self.uploadSource getSize]) forKey:QNReportBlockKeyFileSize];
  230. [item setReportValue:@([QNUtils getCurrentProcessID]) forKey:QNReportBlockKeyPid];
  231. [item setReportValue:@([QNUtils getCurrentThreadID]) forKey:QNReportBlockKeyTid];
  232. [item setReportValue:metrics.metricsList.lastObject.hijacked forKey:QNReportBlockKeyHijacking];
  233. // 统计当前 region 上传速度 文件大小 / 总耗时
  234. if (self.uploadDataErrorResponseInfo == nil && [self.uploadSource getSize] > 0 && [metrics totalElapsedTime] > 0) {
  235. NSNumber *speed = [QNUtils calculateSpeed:[self.uploadSource getSize] totalTime:[metrics totalElapsedTime].longLongValue];
  236. [item setReportValue:speed forKey:QNReportBlockKeyPerceptiveSpeed];
  237. }
  238. if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
  239. [item setReportValue:@(1) forKey:QNReportBlockKeyUpApiVersion];
  240. } else {
  241. [item setReportValue:@(2) forKey:QNReportBlockKeyUpApiVersion];
  242. }
  243. [item setReportValue:[QNUtils getCurrentNetworkType] forKey:QNReportBlockKeyClientTime];
  244. [item setReportValue:[QNUtils systemName] forKey:QNReportBlockKeyOsName];
  245. [item setReportValue:[QNUtils systemVersion] forKey:QNReportBlockKeyOsVersion];
  246. [item setReportValue:[QNUtils sdkLanguage] forKey:QNReportBlockKeySDKName];
  247. [item setReportValue:[QNUtils sdkVersion] forKey:QNReportBlockKeySDKVersion];
  248. [kQNReporter reportItem:item token:self.token.token];
  249. }
  250. - (NSString *)upType {
  251. if (self.config == nil) {
  252. return nil;
  253. }
  254. NSString *sourceType = @"";
  255. if ([self.uploadSource respondsToSelector:@selector(sourceType)]) {
  256. sourceType = [self.uploadSource sourceType];
  257. }
  258. if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
  259. return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV1, sourceType];
  260. } else {
  261. return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV2, sourceType];
  262. }
  263. }
  264. @end