// // QNPartsUpload.m // QiniuSDK_Mac // // Created by yangsen on 2020/5/7. // Copyright © 2020 Qiniu. All rights reserved. // #import "QNDefine.h" #import "QNUtils.h" #import "QNLogUtil.h" #import "QNPartsUpload.h" #import "QNZoneInfo.h" #import "QNReportItem.h" #import "QNRequestTransaction.h" #import "QNPartsUploadPerformerV1.h" #import "QNPartsUploadPerformerV2.h" #define kQNRecordFileInfoKey @"recordFileInfo" #define kQNRecordZoneInfoKey @"recordZoneInfo" @interface QNPartsUpload() @property(nonatomic, strong)QNPartsUploadPerformer *uploadPerformer; @property( atomic, strong)QNResponseInfo *uploadDataErrorResponseInfo; @property( atomic, strong)NSDictionary *uploadDataErrorResponse; @end @implementation QNPartsUpload - (void)initData { [super initData]; // 根据文件从本地恢复上传信息,如果没有则重新构建上传信息 if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) { QNLogInfo(@"key:%@ 分片V1", self.key); self.uploadPerformer = [[QNPartsUploadPerformerV1 alloc] initWithSource:self.uploadSource fileName:self.fileName key:self.key token:self.token option:self.option configuration:self.config recorderKey:self.recorderKey]; } else { QNLogInfo(@"key:%@ 分片V2", self.key); self.uploadPerformer = [[QNPartsUploadPerformerV2 alloc] initWithSource:self.uploadSource fileName:self.fileName key:self.key token:self.token option:self.option configuration:self.config recorderKey:self.recorderKey]; } } - (BOOL)isAllUploaded { return [self.uploadPerformer.uploadInfo isAllUploaded]; } - (void)setErrorResponseInfo:(QNResponseInfo *)responseInfo errorResponse:(NSDictionary *)response{ if (!responseInfo) { return; } if (!self.uploadDataErrorResponseInfo || responseInfo.statusCode != kQNSDKInteriorError) { self.uploadDataErrorResponseInfo = responseInfo; self.uploadDataErrorResponse = response ?: responseInfo.responseDictionary; } } - (int)prepareToUpload{ int code = [super prepareToUpload]; if (code != 0) { return code; } // 配置当前region if (self.uploadPerformer.currentRegion && self.uploadPerformer.currentRegion.isValid) { // currentRegion有值,为断点续传,将region插入至regionList第一处 [self insertRegionAtFirst:self.uploadPerformer.currentRegion]; QNLogInfo(@"key:%@ 使用缓存region", self.key); } else { // currentRegion无值 切换region [self.uploadPerformer switchRegion:[self getCurrentRegion]]; } QNLogInfo(@"key:%@ region:%@", self.key, self.uploadPerformer.currentRegion.zoneInfo.regionId); if (self.uploadSource == nil) { code = kQNLocalIOError; } return code; } - (BOOL)switchRegion{ BOOL isSuccess = [super switchRegion]; if (isSuccess) { [self.uploadPerformer switchRegion:self.getCurrentRegion]; QNLogInfo(@"key:%@ 切换region:%@", self.key , self.uploadPerformer.currentRegion.zoneInfo.regionId); } return isSuccess; } - (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo { [self reportBlock]; return [super switchRegionAndUploadIfNeededWithErrorResponse:errorResponseInfo]; } - (BOOL)reloadUploadInfo { if (![super reloadUploadInfo]) { return NO; } // 重新加载资源 return [self.uploadPerformer couldReloadInfo] && [self.uploadPerformer reloadInfo]; } - (void)startToUpload{ [super startToUpload]; // 重置错误信息 self.uploadDataErrorResponseInfo = nil; self.uploadDataErrorResponse = nil; QNLogInfo(@"key:%@ serverInit", self.key); // 1. 启动upload kQNWeakSelf; [self serverInit:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) { kQNStrongSelf; if (!responseInfo.isOK) { if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) { [self complete:responseInfo response:response]; } return; } QNLogInfo(@"key:%@ uploadRestData", self.key); // 2. 上传数据 kQNWeakSelf; [self uploadRestData:^{ kQNStrongSelf; if (![self isAllUploaded]) { if (![self switchRegionAndUploadIfNeededWithErrorResponse:self.uploadDataErrorResponseInfo]) { [self complete:self.uploadDataErrorResponseInfo response:self.uploadDataErrorResponse]; } return; } // 只有再读取结束再能知道文件大小,需要检测 if ([self.uploadPerformer.uploadInfo getSourceSize] == 0) { QNResponseInfo *responseInfo = [QNResponseInfo responseInfoOfZeroData:@"file is empty"]; [self complete:responseInfo response:responseInfo.responseDictionary]; return; } QNLogInfo(@"key:%@ completeUpload errorResponseInfo:%@", self.key, self.uploadDataErrorResponseInfo); // 3. 组装文件 kQNWeakSelf; [self completeUpload:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) { kQNStrongSelf; if (!responseInfo.isOK) { if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) { [self complete:responseInfo response:response]; } return; } [self complete:responseInfo response:response]; }]; }]; }]; } - (void)uploadRestData:(dispatch_block_t)completeHandler { QNLogInfo(@"key:%@ 串行分片", self.key); [self performUploadRestData:completeHandler]; } - (void)performUploadRestData:(dispatch_block_t)completeHandler { if ([self isAllUploaded]) { completeHandler(); return; } kQNWeakSelf; [self uploadNextData:^(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) { kQNStrongSelf; if (stop || !responseInfo.isOK) { completeHandler(); } else { [self performUploadRestData:completeHandler]; } }]; } //MARK:-- concurrent upload model API - (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler { kQNWeakSelf; void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){ kQNStrongSelf; if (!responseInfo.isOK) { [self setErrorResponseInfo:responseInfo errorResponse:response]; } [self addRegionRequestMetricsOfOneFlow:metrics]; completeHandler(responseInfo, response); }; [self.uploadPerformer serverInit:completeHandlerP]; } - (void)uploadNextData:(void(^)(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler { kQNWeakSelf; void(^completeHandlerP)(BOOL, QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(BOOL stop, QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){ kQNStrongSelf; if (!responseInfo.isOK) { [self setErrorResponseInfo:responseInfo errorResponse:response]; } [self addRegionRequestMetricsOfOneFlow:metrics]; completeHandler(stop, responseInfo, response); }; [self.uploadPerformer uploadNextData:completeHandlerP]; } - (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler { kQNWeakSelf; void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){ kQNStrongSelf; if (!responseInfo.isOK) { [self setErrorResponseInfo:responseInfo errorResponse:response]; } [self addRegionRequestMetricsOfOneFlow:metrics]; completeHandler(responseInfo, response); }; [self.uploadPerformer completeUpload:completeHandlerP]; } - (void)complete:(QNResponseInfo *)info response:(NSDictionary *)response{ [self.uploadSource close]; if ([self shouldRemoveUploadInfoRecord:info]) { [self.uploadPerformer removeUploadInfoRecord]; } [super complete:info response:response]; [self reportBlock]; } - (BOOL)shouldRemoveUploadInfoRecord:(QNResponseInfo *)info { return info.isOK || info.statusCode == 612 || info.statusCode == 614 || info.statusCode == 701; } //MARK:-- 统计block日志 - (void)reportBlock{ QNUploadRegionRequestMetrics *metrics = self.currentRegionRequestMetrics ?: [QNUploadRegionRequestMetrics emptyMetrics]; QNReportItem *item = [QNReportItem item]; [item setReportValue:QNReportLogTypeBlock forKey:QNReportBlockKeyLogType]; [item setReportValue:@([[NSDate date] timeIntervalSince1970]) forKey:QNReportBlockKeyUpTime]; [item setReportValue:self.token.bucket forKey:QNReportBlockKeyTargetBucket]; [item setReportValue:self.key forKey:QNReportBlockKeyTargetKey]; [item setReportValue:[self getTargetRegion].zoneInfo.regionId forKey:QNReportBlockKeyTargetRegionId]; [item setReportValue:[self getCurrentRegion].zoneInfo.regionId forKey:QNReportBlockKeyCurrentRegionId]; [item setReportValue:metrics.totalElapsedTime forKey:QNReportBlockKeyTotalElapsedTime]; [item setReportValue:metrics.bytesSend forKey:QNReportBlockKeyBytesSent]; [item setReportValue:self.uploadPerformer.recoveredFrom forKey:QNReportBlockKeyRecoveredFrom]; [item setReportValue:@([self.uploadSource getSize]) forKey:QNReportBlockKeyFileSize]; [item setReportValue:@([QNUtils getCurrentProcessID]) forKey:QNReportBlockKeyPid]; [item setReportValue:@([QNUtils getCurrentThreadID]) forKey:QNReportBlockKeyTid]; [item setReportValue:metrics.metricsList.lastObject.hijacked forKey:QNReportBlockKeyHijacking]; // 统计当前 region 上传速度 文件大小 / 总耗时 if (self.uploadDataErrorResponseInfo == nil && [self.uploadSource getSize] > 0 && [metrics totalElapsedTime] > 0) { NSNumber *speed = [QNUtils calculateSpeed:[self.uploadSource getSize] totalTime:[metrics totalElapsedTime].longLongValue]; [item setReportValue:speed forKey:QNReportBlockKeyPerceptiveSpeed]; } if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) { [item setReportValue:@(1) forKey:QNReportBlockKeyUpApiVersion]; } else { [item setReportValue:@(2) forKey:QNReportBlockKeyUpApiVersion]; } [item setReportValue:[QNUtils getCurrentNetworkType] forKey:QNReportBlockKeyClientTime]; [item setReportValue:[QNUtils systemName] forKey:QNReportBlockKeyOsName]; [item setReportValue:[QNUtils systemVersion] forKey:QNReportBlockKeyOsVersion]; [item setReportValue:[QNUtils sdkLanguage] forKey:QNReportBlockKeySDKName]; [item setReportValue:[QNUtils sdkVersion] forKey:QNReportBlockKeySDKVersion]; [kQNReporter reportItem:item token:self.token.token]; } - (NSString *)upType { if (self.config == nil) { return nil; } NSString *sourceType = @""; if ([self.uploadSource respondsToSelector:@selector(sourceType)]) { sourceType = [self.uploadSource sourceType]; } if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) { return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV1, sourceType]; } else { return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV2, sourceType]; } } @end