QNUploadManager.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. //
  2. // QNUploader.h
  3. // QiniuSDK
  4. //
  5. // Created by bailong on 14-9-28.
  6. // Copyright (c) 2014年 Qiniu. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #if __IPHONE_OS_VERSION_MIN_REQUIRED
  10. #import <MobileCoreServices/MobileCoreServices.h>
  11. #import <UIKit/UIKit.h>
  12. #if !TARGET_OS_MACCATALYST
  13. #import <AssetsLibrary/AssetsLibrary.h>
  14. #import "QNALAssetFile.h"
  15. #endif
  16. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
  17. #import "QNPHAssetFile.h"
  18. #import <Photos/Photos.h>
  19. #endif
  20. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
  21. #import "QNPHAssetResource.h"
  22. #endif
  23. #else
  24. #import <CoreServices/CoreServices.h>
  25. #endif
  26. #import "QNAsyncRun.h"
  27. #import "QNConfiguration.h"
  28. #import "QNCrc32.h"
  29. #import "QNFile.h"
  30. #import "QNUtils.h"
  31. #import "QNResponseInfo.h"
  32. #import "QNFormUpload.h"
  33. #import "QNPartsUpload.h"
  34. #import "QNConcurrentResumeUpload.h"
  35. #import "QNUpToken.h"
  36. #import "QNUploadOption.h"
  37. #import "QNReportItem.h"
  38. #import "QNDnsPrefetch.h"
  39. #import "QNZone.h"
  40. @interface QNUploadManager ()
  41. @property (nonatomic) QNConfiguration *config;
  42. @end
  43. @implementation QNUploadManager
  44. - (instancetype)init {
  45. return [self initWithConfiguration:nil];
  46. }
  47. - (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder {
  48. return [self initWithRecorder:recorder recorderKeyGenerator:nil];
  49. }
  50. - (instancetype)initWithRecorder:(id<QNRecorderDelegate>)recorder
  51. recorderKeyGenerator:(QNRecorderKeyGenerator)recorderKeyGenerator {
  52. QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
  53. builder.recorder = recorder;
  54. builder.recorderKeyGen = recorderKeyGenerator;
  55. }];
  56. return [self initWithConfiguration:config];
  57. }
  58. - (instancetype)initWithConfiguration:(QNConfiguration *)config {
  59. if (self = [super init]) {
  60. if (config == nil) {
  61. config = [QNConfiguration build:^(QNConfigurationBuilder *builder){
  62. }];
  63. }
  64. _config = config;
  65. [[QNTransactionManager shared] addDnsLocalLoadTransaction];
  66. }
  67. return self;
  68. }
  69. + (instancetype)sharedInstanceWithConfiguration:(QNConfiguration *)config {
  70. static QNUploadManager *sharedInstance = nil;
  71. static dispatch_once_t onceToken;
  72. dispatch_once(&onceToken, ^{
  73. sharedInstance = [[self alloc] initWithConfiguration:config];
  74. });
  75. return sharedInstance;
  76. }
  77. - (void)putData:(NSData *)data
  78. key:(NSString *)key
  79. token:(NSString *)token
  80. complete:(QNUpCompletionHandler)completionHandler
  81. option:(QNUploadOption *)option {
  82. [self putData:data fileName:nil key:key token:token complete:completionHandler option:option];
  83. }
  84. - (void)putData:(NSData *)data
  85. fileName:(NSString *)fileName
  86. key:(NSString *)key
  87. token:(NSString *)token
  88. complete:(QNUpCompletionHandler)completionHandler
  89. option:(QNUploadOption *)option {
  90. if ([QNUploadManager checkAndNotifyError:key token:token input:data complete:completionHandler]) {
  91. return;
  92. }
  93. QNUpToken *t = [QNUpToken parse:token];
  94. if (t == nil || ![t isValid]) {
  95. QNResponseInfo *info = [QNResponseInfo responseInfoWithInvalidToken:@"invalid token"];
  96. [QNUploadManager complete:token
  97. key:key
  98. responseInfo:info
  99. response:nil
  100. taskMetrics:nil
  101. complete:completionHandler];
  102. return;
  103. }
  104. [[QNTransactionManager shared] addDnsCheckAndPrefetchTransaction:self.config.zone token:t];
  105. QNUpTaskCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, QNUploadTaskMetrics *metrics, NSDictionary *resp) {
  106. [QNUploadManager complete:token
  107. key:key
  108. responseInfo:info
  109. response:resp
  110. taskMetrics:metrics
  111. complete:completionHandler];
  112. };
  113. QNFormUpload *up = [[QNFormUpload alloc] initWithData:data
  114. key:key
  115. fileName:fileName
  116. token:t
  117. option:option
  118. configuration:self.config
  119. completionHandler:complete];
  120. QNAsyncRun(^{
  121. [up run];
  122. });
  123. }
  124. - (void)putFileInternal:(id<QNFileDelegate>)file
  125. key:(NSString *)key
  126. token:(NSString *)token
  127. complete:(QNUpCompletionHandler)completionHandler
  128. option:(QNUploadOption *)option {
  129. @autoreleasepool {
  130. QNUpToken *t = [QNUpToken parse:token];
  131. if (t == nil || ![t isValid]) {
  132. QNResponseInfo *info = [QNResponseInfo responseInfoWithInvalidToken:@"invalid token"];
  133. [QNUploadManager complete:token
  134. key:key
  135. responseInfo:info
  136. response:nil
  137. taskMetrics:nil
  138. complete:completionHandler];
  139. return;
  140. }
  141. QNUpTaskCompletionHandler complete = ^(QNResponseInfo *info, NSString *key, QNUploadTaskMetrics *metrics, NSDictionary *resp) {
  142. [file close];
  143. [QNUploadManager complete:token
  144. key:key
  145. responseInfo:info
  146. response:resp
  147. taskMetrics:metrics
  148. complete:completionHandler];
  149. };
  150. [[QNTransactionManager shared] addDnsCheckAndPrefetchTransaction:self.config.zone token:t];
  151. if ([file size] <= self.config.putThreshold) {
  152. NSError *error;
  153. NSData *data = [file readAllWithError:&error];
  154. [file close];
  155. if (error) {
  156. QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
  157. [QNUploadManager complete:token
  158. key:key
  159. responseInfo:info
  160. response:nil
  161. taskMetrics:nil
  162. complete:completionHandler];
  163. return;
  164. }
  165. NSString *fileName = [[file path] lastPathComponent];
  166. [self putData:data
  167. fileName:fileName
  168. key:key
  169. token:token
  170. complete:completionHandler
  171. option:option];
  172. return;
  173. }
  174. NSString *recorderKey = key;
  175. if (self.config.recorder != nil && self.config.recorderKeyGen != nil) {
  176. recorderKey = self.config.recorderKeyGen(key, [file path]);
  177. }
  178. if (self.config.useConcurrentResumeUpload) {
  179. QNConcurrentResumeUpload *up = [[QNConcurrentResumeUpload alloc]
  180. initWithFile:file
  181. key:key
  182. token:t
  183. option:option
  184. configuration:self.config
  185. recorder:self.config.recorder
  186. recorderKey:recorderKey
  187. completionHandler:complete];
  188. QNAsyncRun(^{
  189. [up run];
  190. });
  191. } else {
  192. QNPartsUpload *up = [[QNPartsUpload alloc]
  193. initWithFile:file
  194. key:key
  195. token:t
  196. option:option
  197. configuration:self.config
  198. recorder:self.config.recorder
  199. recorderKey:recorderKey
  200. completionHandler:complete];
  201. QNAsyncRun(^{
  202. [up run];
  203. });
  204. }
  205. }
  206. }
  207. - (void)putFile:(NSString *)filePath
  208. key:(NSString *)key
  209. token:(NSString *)token
  210. complete:(QNUpCompletionHandler)completionHandler
  211. option:(QNUploadOption *)option {
  212. if ([QNUploadManager checkAndNotifyError:key token:token input:filePath complete:completionHandler]) {
  213. return;
  214. }
  215. @autoreleasepool {
  216. NSError *error = nil;
  217. __block QNFile *file = [[QNFile alloc] init:filePath error:&error];
  218. if (error) {
  219. QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
  220. [QNUploadManager complete:token
  221. key:key
  222. responseInfo:info
  223. response:nil
  224. taskMetrics:nil
  225. complete:completionHandler];
  226. return;
  227. }
  228. [self putFileInternal:file key:key token:token complete:completionHandler option:option];
  229. }
  230. }
  231. #if !TARGET_OS_MACCATALYST
  232. - (void)putALAsset:(ALAsset *)asset
  233. key:(NSString *)key
  234. token:(NSString *)token
  235. complete:(QNUpCompletionHandler)completionHandler
  236. option:(QNUploadOption *)option {
  237. #if __IPHONE_OS_VERSION_MIN_REQUIRED
  238. if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
  239. return;
  240. }
  241. @autoreleasepool {
  242. NSError *error = nil;
  243. __block QNALAssetFile *file = [[QNALAssetFile alloc] init:asset error:&error];
  244. if (error) {
  245. QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
  246. [QNUploadManager complete:token
  247. key:key
  248. responseInfo:info
  249. response:nil
  250. taskMetrics:nil
  251. complete:completionHandler];
  252. return;
  253. }
  254. [self putFileInternal:file key:key token:token complete:completionHandler option:option];
  255. }
  256. #endif
  257. }
  258. #endif
  259. - (void)putPHAsset:(PHAsset *)asset
  260. key:(NSString *)key
  261. token:(NSString *)token
  262. complete:(QNUpCompletionHandler)completionHandler
  263. option:(QNUploadOption *)option {
  264. #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90100)
  265. if ([QNUploadManager checkAndNotifyError:key token:token input:asset complete:completionHandler]) {
  266. return;
  267. }
  268. @autoreleasepool {
  269. NSError *error = nil;
  270. __block QNPHAssetFile *file = [[QNPHAssetFile alloc] init:asset error:&error];
  271. if (error) {
  272. QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
  273. [QNUploadManager complete:token
  274. key:key
  275. responseInfo:info
  276. response:nil
  277. taskMetrics:nil
  278. complete:completionHandler];
  279. return;
  280. }
  281. [self putFileInternal:file key:key token:token complete:completionHandler option:option];
  282. }
  283. #endif
  284. }
  285. - (void)putPHAssetResource:(PHAssetResource *)assetResource
  286. key:(NSString *)key
  287. token:(NSString *)token
  288. complete:(QNUpCompletionHandler)completionHandler
  289. option:(QNUploadOption *)option {
  290. #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000)
  291. if ([QNUploadManager checkAndNotifyError:key token:token input:assetResource complete:completionHandler]) {
  292. return;
  293. }
  294. @autoreleasepool {
  295. NSError *error = nil;
  296. __block QNPHAssetResource *file = [[QNPHAssetResource alloc] init:assetResource error:&error];
  297. if (error) {
  298. QNResponseInfo *info = [QNResponseInfo responseInfoWithFileError:error];
  299. [QNUploadManager complete:token
  300. key:key
  301. responseInfo:info
  302. response:nil
  303. taskMetrics:nil
  304. complete:completionHandler];
  305. return;
  306. }
  307. [self putFileInternal:file key:key token:token complete:completionHandler option:option];
  308. }
  309. #endif
  310. }
  311. + (BOOL)checkAndNotifyError:(NSString *)key
  312. token:(NSString *)token
  313. input:(NSObject *)input
  314. complete:(QNUpCompletionHandler)completionHandler {
  315. if (completionHandler == nil) {
  316. @throw [NSException exceptionWithName:NSInvalidArgumentException
  317. reason:@"no completionHandler"
  318. userInfo:nil];
  319. return YES;
  320. }
  321. QNResponseInfo *info = nil;
  322. if (input == nil) {
  323. info = [QNResponseInfo responseInfoOfZeroData:@"no input data"];
  324. } else if ([input isKindOfClass:[NSData class]] && [(NSData *)input length] == 0) {
  325. info = [QNResponseInfo responseInfoOfZeroData:@"no input data"];
  326. } else if (token == nil || [token isEqual:[NSNull null]] || [token isEqualToString:@""]) {
  327. info = [QNResponseInfo responseInfoWithInvalidToken:@"no token"];
  328. }
  329. if (info != nil) {
  330. [QNUploadManager complete:token
  331. key:key
  332. responseInfo:info
  333. response:nil
  334. taskMetrics:nil
  335. complete:completionHandler];
  336. return YES;
  337. } else {
  338. return NO;
  339. }
  340. }
  341. + (void)complete:(NSString *)token
  342. key:(NSString *)key
  343. responseInfo:(QNResponseInfo *)responseInfo
  344. response:(NSDictionary *)response
  345. taskMetrics:(QNUploadTaskMetrics *)taskMetrics
  346. complete:(QNUpCompletionHandler)completionHandler {
  347. [QNUploadManager reportQuality:key responseInfo:responseInfo taskMetrics:taskMetrics token:token];
  348. QNAsyncRunInMain(^{
  349. if (completionHandler) {
  350. completionHandler(responseInfo, key, response);
  351. }
  352. });
  353. }
  354. //MARK:-- 统计quality日志
  355. + (void)reportQuality:(NSString *)key
  356. responseInfo:(QNResponseInfo *)responseInfo
  357. taskMetrics:(QNUploadTaskMetrics *)taskMetrics
  358. token:(NSString *)token{
  359. QNUpToken *upToken = [QNUpToken parse:token];
  360. QNUploadTaskMetrics *taskMetricsP = taskMetrics ?: [QNUploadTaskMetrics emptyMetrics];
  361. QNReportItem *item = [QNReportItem item];
  362. [item setReportValue:QNReportLogTypeQuality forKey:QNReportQualityKeyLogType];
  363. [item setReportValue:@([[NSDate date] timeIntervalSince1970]) forKey:QNReportQualityKeyUpTime];
  364. [item setReportValue:responseInfo.qualityResult forKey:QNReportQualityKeyResult];
  365. [item setReportValue:upToken.bucket forKey:QNReportQualityKeyTargetBucket];
  366. [item setReportValue:key forKey:QNReportQualityKeyTargetKey];
  367. [item setReportValue:taskMetricsP.totalElapsedTime forKey:QNReportQualityKeyTotalElapsedTime];
  368. [item setReportValue:taskMetricsP.totalElapsedTime forKey:QNReportQualityKeyTotalElapsedTime];
  369. [item setReportValue:taskMetricsP.requestCount forKey:QNReportQualityKeyRequestsCount];
  370. [item setReportValue:taskMetricsP.regionCount forKey:QNReportQualityKeyRegionsCount];
  371. [item setReportValue:taskMetricsP.bytesSend forKey:QNReportQualityKeyBytesSent];
  372. [item setReportValue:[QNUtils systemName] forKey:QNReportQualityKeyOsName];
  373. [item setReportValue:[QNUtils systemVersion] forKey:QNReportQualityKeyOsVersion];
  374. [item setReportValue:[QNUtils sdkLanguage] forKey:QNReportQualityKeySDKName];
  375. [item setReportValue:[QNUtils sdkVersion] forKey:QNReportQualityKeySDKVersion];
  376. [item setReportValue:responseInfo.requestReportErrorType forKey:QNReportQualityKeyErrorType];
  377. NSString *errorDesc = responseInfo.requestReportErrorType ? responseInfo.message : nil;
  378. [item setReportValue:errorDesc forKey:QNReportQualityKeyErrorDescription];
  379. [kQNReporter reportItem:item token:token];
  380. }
  381. @end