QNUploadInfoV1.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. //
  2. // QNUploadInfoV1.m
  3. // QiniuSDK
  4. //
  5. // Created by yangsen on 2021/5/10.
  6. // Copyright © 2021 Qiniu. All rights reserved.
  7. //
  8. #import "NSData+QNMD5.h"
  9. #import "QNMutableArray.h"
  10. #import "QNUploadInfoV1.h"
  11. #define kTypeValue @"UploadInfoV1"
  12. #define kBlockSize (4 * 1024 * 1024)
  13. @interface QNUploadInfoV1()
  14. @property(nonatomic, assign)int dataSize;
  15. @property(nonatomic, strong)QNMutableArray *blockList;
  16. @property(nonatomic, assign)BOOL isEOF;
  17. @property(nonatomic, strong, nullable)NSError *readError;
  18. @end
  19. @implementation QNUploadInfoV1
  20. + (instancetype)info:(id<QNUploadSource>)source
  21. configuration:(nonnull QNConfiguration *)configuration {
  22. QNUploadInfoV1 *info = [QNUploadInfoV1 info:source];
  23. if (configuration.useConcurrentResumeUpload || configuration.chunkSize > kBlockSize) {
  24. info.dataSize = kBlockSize;
  25. } else {
  26. info.dataSize = configuration.chunkSize;
  27. }
  28. info.blockList = [QNMutableArray array];
  29. return info;
  30. }
  31. + (instancetype)info:(id <QNUploadSource>)source
  32. dictionary:(NSDictionary *)dictionary {
  33. if (dictionary == nil) {
  34. return nil;
  35. }
  36. int dataSize = [dictionary[@"dataSize"] intValue];
  37. NSString *type = dictionary[kQNUploadInfoTypeKey];
  38. NSArray *blockInfoList = dictionary[@"blockList"];
  39. QNMutableArray *blockList = [QNMutableArray array];
  40. if ([blockInfoList isKindOfClass:[NSArray class]]) {
  41. for (int i = 0; i < blockInfoList.count; i++) {
  42. NSDictionary *blockInfo = blockInfoList[i];
  43. if ([blockInfo isKindOfClass:[NSDictionary class]]) {
  44. QNUploadBlock *block = [QNUploadBlock blockFromDictionary:blockInfo];
  45. if (block == nil) {
  46. return nil;
  47. }
  48. [blockList addObject:block];
  49. }
  50. }
  51. }
  52. QNUploadInfoV1 *info = [QNUploadInfoV1 info:source];
  53. [info setInfoFromDictionary:dictionary];
  54. info.dataSize = dataSize;
  55. info.blockList = blockList;
  56. if (![type isEqualToString:kTypeValue] || ![[source getId] isEqualToString:[info getSourceId]]) {
  57. return nil;
  58. } else {
  59. return info;
  60. }
  61. }
  62. - (BOOL)isFirstData:(QNUploadData *)data {
  63. return data.index == 0;
  64. }
  65. - (BOOL)isValid {
  66. if (![super isValid]) {
  67. return false;
  68. }
  69. __block BOOL valid = true;
  70. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  71. valid = [block isValid];
  72. if (!valid) {
  73. *stop = true;
  74. }
  75. }];
  76. return valid;
  77. }
  78. - (BOOL)reloadSource {
  79. self.isEOF = false;
  80. self.readError = nil;
  81. return [super reloadSource];
  82. }
  83. - (BOOL)isSameUploadInfo:(QNUploadInfo *)info {
  84. if (![super isSameUploadInfo:info]) {
  85. return false;
  86. }
  87. if (![info isKindOfClass:[QNUploadInfoV1 class]]) {
  88. return false;
  89. }
  90. return self.dataSize == [(QNUploadInfoV1 *)info dataSize];
  91. }
  92. - (void)clearUploadState {
  93. if (self.blockList == nil || self.blockList.count == 0) {
  94. return;
  95. }
  96. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  97. [block clearUploadState];
  98. }];
  99. }
  100. - (void)checkInfoStateAndUpdate {
  101. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  102. [block checkInfoStateAndUpdate];
  103. }];
  104. }
  105. - (long long)uploadSize {
  106. if (self.blockList == nil || self.blockList.count == 0) {
  107. return 0;
  108. }
  109. __block long long uploadSize = 0;
  110. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  111. uploadSize += [block uploadSize];
  112. }];
  113. return uploadSize;
  114. }
  115. - (BOOL)isAllUploaded {
  116. if (!_isEOF) {
  117. return false;
  118. }
  119. if (self.blockList == nil || self.blockList.count == 0) {
  120. return true;
  121. }
  122. __block BOOL isAllUploaded = true;
  123. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  124. if (!block.isCompleted) {
  125. isAllUploaded = false;
  126. *stop = true;
  127. }
  128. }];
  129. return isAllUploaded;
  130. }
  131. - (NSDictionary *)toDictionary {
  132. NSMutableDictionary *dictionary = [[super toDictionary] mutableCopy];
  133. if (dictionary == nil) {
  134. dictionary = [NSMutableDictionary dictionary];
  135. }
  136. [dictionary setObject:kTypeValue forKey:kQNUploadInfoTypeKey];
  137. [dictionary setObject:@(self.dataSize) forKey:@"dataSize"];
  138. if (self.blockList != nil && self.blockList.count != 0) {
  139. NSMutableArray *blockInfoList = [NSMutableArray array];
  140. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  141. [blockInfoList addObject:[block toDictionary]];
  142. }];
  143. [dictionary setObject:[blockInfoList copy] forKey:@"blockList"];
  144. }
  145. return [dictionary copy];
  146. }
  147. - (QNUploadBlock *)nextUploadBlock:(NSError **)error {
  148. // 从 blockList 中读取需要上传的 block
  149. QNUploadBlock *block = [self nextUploadBlockFormBlockList];
  150. // 内存的 blockList 中没有可上传的数据,则从资源中读并创建 block
  151. if (block == nil) {
  152. if (self.isEOF) {
  153. return nil;
  154. } else if (self.readError) {
  155. *error = self.readError;
  156. return nil;
  157. }
  158. // 从资源中读取新的 block 进行上传
  159. long long blockOffset = 0;
  160. if (self.blockList.count > 0) {
  161. QNUploadBlock *lastBlock = self.blockList[self.blockList.count - 1];
  162. blockOffset = lastBlock.offset + (long long)(lastBlock.size);
  163. }
  164. block = [[QNUploadBlock alloc] initWithOffset:blockOffset blockSize:kBlockSize dataSize:self.dataSize index:self.blockList.count];
  165. }
  166. QNUploadBlock *loadBlock = [self loadBlockData:block error:error];
  167. if (*error != nil) {
  168. self.readError = *error;
  169. return nil;
  170. }
  171. if (loadBlock == nil) {
  172. // 没有加在到 block, 也即数据源读取结束
  173. self.isEOF = true;
  174. // 有多余的 block 则移除,移除中包含 block
  175. if (self.blockList.count > block.index) {
  176. self.blockList = [[self.blockList subarrayWithRange:NSMakeRange(0, block.index)] mutableCopy];
  177. }
  178. } else {
  179. // 加在到 block
  180. if (loadBlock.index == self.blockList.count) {
  181. // 新块:block index 等于 blockList size 则为新创建 block,需要加入 blockList
  182. [self.blockList addObject:loadBlock];
  183. } else if (loadBlock != block) {
  184. // 更换块:重新加在了 block, 更换信息
  185. [self.blockList replaceObjectAtIndex:loadBlock.index withObject:loadBlock];
  186. }
  187. // 数据源读取结束,块读取大小小于预期,读取结束
  188. if (loadBlock.size < kBlockSize) {
  189. self.isEOF = true;
  190. // 有多余的 block 则移除,移除中不包含 block
  191. if (self.blockList.count > block.index + 1) {
  192. self.blockList = [[self.blockList subarrayWithRange:NSMakeRange(0, block.index + 1)] mutableCopy];
  193. }
  194. }
  195. }
  196. return loadBlock;
  197. }
  198. - (QNUploadBlock *)nextUploadBlockFormBlockList {
  199. if (self.blockList == nil || self.blockList.count == 0) {
  200. return nil;
  201. }
  202. __block QNUploadBlock *block = nil;
  203. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *blockP, NSUInteger idx, BOOL * _Nonnull stop) {
  204. QNUploadData *data = [blockP nextUploadDataWithoutCheckData];
  205. if (data != nil) {
  206. block = blockP;
  207. *stop = true;
  208. }
  209. }];
  210. return block;
  211. }
  212. // 加载块中的数据
  213. // 1. 数据块已加载,直接返回
  214. // 2. 数据块未加载,读块数据
  215. // 2.1 如果未读到数据,则已 EOF,返回 null
  216. // 2.2 如果读到数据
  217. // 2.2.1 如果块数据符合预期,则当片未上传,则加载片数据
  218. // 2.2.2 如果块数据不符合预期,创建新块,加载片信息
  219. - (QNUploadBlock *)loadBlockData:(QNUploadBlock *)block error:(NSError **)error {
  220. if (block == nil) {
  221. return nil;
  222. }
  223. // 已经加载过 block 数据
  224. // 没有需要上传的片 或者 有需要上传片但是已加载过片数据
  225. QNUploadData *nextUploadData = [block nextUploadDataWithoutCheckData];
  226. if (nextUploadData.state == QNUploadStateWaitToUpload && nextUploadData.data != nil) {
  227. return block;
  228. }
  229. // 未加载过 block 数据
  230. // 根据 block 信息加载 blockBytes
  231. NSData *blockBytes = nil;
  232. blockBytes = [self readData:block.size dataOffset:block.offset error:error];
  233. if (*error != nil) {
  234. return nil;
  235. }
  236. // 没有数据不需要上传
  237. if (blockBytes == nil || blockBytes.length == 0) {
  238. return nil;
  239. }
  240. NSString *md5 = [blockBytes qn_md5];
  241. // 判断当前 block 的数据是否和实际数据吻合,不吻合则之前 block 被抛弃,重新创建 block
  242. if (blockBytes.length != block.size || block.md5 == nil || ![block.md5 isEqualToString:md5]) {
  243. block = [[QNUploadBlock alloc] initWithOffset:block.offset blockSize:blockBytes.length dataSize:self.dataSize index:block.index];
  244. block.md5 = md5;
  245. }
  246. for (QNUploadData *data in block.uploadDataList) {
  247. if (data.state != QNUploadStateComplete) {
  248. // 还未上传的
  249. data.data = [blockBytes subdataWithRange:NSMakeRange((NSUInteger)data.offset, (NSUInteger)data.size)];
  250. data.state = QNUploadStateWaitToUpload;
  251. } else {
  252. // 已经上传的
  253. data.state = QNUploadStateComplete;
  254. }
  255. }
  256. return block;
  257. }
  258. - (QNUploadData *)nextUploadData:(QNUploadBlock *)block {
  259. if (block == nil) {
  260. return nil;
  261. }
  262. return [block nextUploadDataWithoutCheckData];
  263. }
  264. - (NSArray <NSString *> *)allBlocksContexts {
  265. if (self.blockList == nil || self.blockList.count == 0) {
  266. return nil;
  267. }
  268. NSMutableArray *contexts = [NSMutableArray array];
  269. [self.blockList enumerateObjectsUsingBlock:^(QNUploadBlock *block, NSUInteger idx, BOOL * _Nonnull stop) {
  270. if (block.context && block.context.length > 0) {
  271. [contexts addObject:block.context];
  272. }
  273. }];
  274. return [contexts copy];
  275. }
  276. @end