advance.js 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521
  1. var session = require('./session');
  2. var Async = require('./async');
  3. var EventProxy = require('./event').EventProxy;
  4. var util = require('./util');
  5. var Tracker = require('./tracker');
  6. // 文件分块上传全过程,暴露的分块上传接口
  7. function sliceUploadFile(params, callback) {
  8. var self = this;
  9. // 如果小程序版本不支持获取文件分片内容,统一转到 简单上传 接口上传
  10. if (!util.canFileSlice()) {
  11. params.SkipTask = true;
  12. if (self.options.SimpleUploadMethod === 'postObject') {
  13. self.postObject(params, callback);
  14. } else {
  15. self.putObject(params, callback);
  16. }
  17. return;
  18. }
  19. var ep = new EventProxy();
  20. var TaskId = params.TaskId;
  21. var Bucket = params.Bucket;
  22. var Region = params.Region;
  23. var Key = params.Key;
  24. var FilePath = params.FilePath;
  25. var ChunkSize = params.ChunkSize || params.SliceSize || self.options.ChunkSize;
  26. var AsyncLimit = params.AsyncLimit;
  27. var StorageClass = params.StorageClass;
  28. var ServerSideEncryption = params.ServerSideEncryption;
  29. var FileSize;
  30. var onProgress;
  31. var onHashProgress = params.onHashProgress;
  32. var tracker = params.tracker;
  33. tracker && tracker.setParams({ chunkSize: ChunkSize });
  34. // 上传过程中出现错误,返回错误
  35. ep.on('error', function (err) {
  36. if (!self._isRunningTask(TaskId)) return;
  37. var _err = {
  38. UploadId: params.UploadData.UploadId || '',
  39. err: err,
  40. error: err,
  41. };
  42. return callback(_err);
  43. });
  44. // 上传分块完成,开始 uploadSliceComplete 操作
  45. ep.on('upload_complete', function (UploadCompleteData) {
  46. var _UploadCompleteData = util.extend(
  47. {
  48. UploadId: params.UploadData.UploadId || '',
  49. },
  50. UploadCompleteData
  51. );
  52. callback(null, _UploadCompleteData);
  53. });
  54. // 上传分块完成,开始 uploadSliceComplete 操作
  55. ep.on('upload_slice_complete', function (UploadData) {
  56. var metaHeaders = {};
  57. util.each(params.Headers, function (val, k) {
  58. var shortKey = k.toLowerCase();
  59. if (shortKey.indexOf('x-cos-meta-') === 0 || shortKey === 'pic-operations') {
  60. metaHeaders[k] = val;
  61. }
  62. });
  63. uploadSliceComplete.call(
  64. self,
  65. {
  66. Bucket: Bucket,
  67. Region: Region,
  68. Key: Key,
  69. UploadId: UploadData.UploadId,
  70. SliceList: UploadData.SliceList,
  71. Headers: metaHeaders,
  72. tracker: tracker,
  73. },
  74. function (err, data) {
  75. if (!self._isRunningTask(TaskId)) return;
  76. session.removeUsing(UploadData.UploadId);
  77. if (err) {
  78. onProgress(null, true);
  79. return ep.emit('error', err);
  80. }
  81. session.removeUploadId(UploadData.UploadId);
  82. onProgress({ loaded: FileSize, total: FileSize }, true);
  83. ep.emit('upload_complete', data);
  84. }
  85. );
  86. });
  87. // 获取 UploadId 完成,开始上传每个分片
  88. ep.on('get_upload_data_finish', function (UploadData) {
  89. // 处理 UploadId 缓存
  90. var uuid = session.getFileId(params.FileStat, params.ChunkSize, Bucket, Key);
  91. uuid && session.saveUploadId(uuid, UploadData.UploadId, self.options.UploadIdCacheLimit); // 缓存 UploadId
  92. session.setUsing(UploadData.UploadId); // 标记 UploadId 为正在使用
  93. // 获取 UploadId
  94. onProgress(null, true); // 任务状态开始 uploading
  95. uploadSliceList.call(
  96. self,
  97. {
  98. TaskId: TaskId,
  99. Bucket: Bucket,
  100. Region: Region,
  101. Key: Key,
  102. FilePath: FilePath,
  103. FileSize: FileSize,
  104. SliceSize: ChunkSize,
  105. AsyncLimit: AsyncLimit,
  106. ServerSideEncryption: ServerSideEncryption,
  107. UploadData: UploadData,
  108. onProgress: onProgress,
  109. tracker: tracker,
  110. },
  111. function (err, data) {
  112. if (!self._isRunningTask(TaskId)) return;
  113. if (err) {
  114. onProgress(null, true);
  115. return ep.emit('error', err);
  116. }
  117. ep.emit('upload_slice_complete', data);
  118. }
  119. );
  120. });
  121. // 开始获取文件 UploadId,里面会视情况计算 ETag,并比对,保证文件一致性,也优化上传
  122. ep.on('get_file_size_finish', function () {
  123. onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
  124. if (params.UploadData.UploadId) {
  125. ep.emit('get_upload_data_finish', params.UploadData);
  126. } else {
  127. var _params = util.extend(
  128. {
  129. TaskId: TaskId,
  130. Bucket: Bucket,
  131. Region: Region,
  132. Key: Key,
  133. Headers: params.Headers,
  134. StorageClass: StorageClass,
  135. FilePath: FilePath,
  136. FileSize: FileSize,
  137. SliceSize: ChunkSize,
  138. onHashProgress: onHashProgress,
  139. tracker: tracker,
  140. },
  141. params
  142. );
  143. // 这里用户传入的params.FileSize可能单位不统一,必须使用sdk内获取的大小
  144. _params.FileSize = FileSize;
  145. getUploadIdAndPartList.call(self, _params, function (err, UploadData) {
  146. if (!self._isRunningTask(TaskId)) return;
  147. if (err) return ep.emit('error', err);
  148. params.UploadData.UploadId = UploadData.UploadId;
  149. params.UploadData.PartList = UploadData.PartList;
  150. ep.emit('get_upload_data_finish', params.UploadData);
  151. });
  152. }
  153. });
  154. // 获取上传文件大小
  155. FileSize = params.ContentLength;
  156. delete params.ContentLength;
  157. !params.Headers && (params.Headers = {});
  158. util.each(params.Headers, function (item, key) {
  159. if (key.toLowerCase() === 'content-length') {
  160. delete params.Headers[key];
  161. }
  162. });
  163. // 控制分片大小
  164. (function () {
  165. var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
  166. var AutoChunkSize = 1024 * 1024;
  167. for (var i = 0; i < SIZE.length; i++) {
  168. AutoChunkSize = SIZE[i] * 1024 * 1024;
  169. if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
  170. }
  171. params.ChunkSize = params.SliceSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
  172. })();
  173. // 开始上传
  174. if (FileSize === 0) {
  175. params.Body = '';
  176. params.ContentLength = 0;
  177. params.SkipTask = true;
  178. self.putObject(params, function (err, data) {
  179. if (err) {
  180. return callback(err);
  181. }
  182. callback(null, data);
  183. });
  184. } else {
  185. ep.emit('get_file_size_finish');
  186. }
  187. }
  188. // 获取上传任务的 UploadId
  189. function getUploadIdAndPartList(params, callback) {
  190. var TaskId = params.TaskId;
  191. var Bucket = params.Bucket;
  192. var Region = params.Region;
  193. var Key = params.Key;
  194. var StorageClass = params.StorageClass;
  195. var self = this;
  196. // 计算 ETag
  197. var ETagMap = {};
  198. var FileSize = params.FileSize;
  199. var SliceSize = params.SliceSize;
  200. var SliceCount = Math.ceil(FileSize / SliceSize);
  201. var FinishSliceCount = 0;
  202. var FinishSize = 0;
  203. var onHashProgress = util.throttleOnProgress.call(self, FileSize, params.onHashProgress);
  204. var getChunkETag = function (PartNumber, callback) {
  205. var start = SliceSize * (PartNumber - 1);
  206. var end = Math.min(start + SliceSize, FileSize);
  207. var ChunkSize = end - start;
  208. if (ETagMap[PartNumber]) {
  209. callback(null, {
  210. PartNumber: PartNumber,
  211. ETag: ETagMap[PartNumber],
  212. Size: ChunkSize,
  213. });
  214. } else {
  215. util.fileSlice(params.FilePath, start, end, function (chunkItem) {
  216. try {
  217. var md5 = util.getFileMd5(chunkItem);
  218. } catch (err) {
  219. return callback(err);
  220. }
  221. var ETag = '"' + md5 + '"';
  222. ETagMap[PartNumber] = ETag;
  223. FinishSliceCount += 1;
  224. FinishSize += ChunkSize;
  225. callback(null, {
  226. PartNumber: PartNumber,
  227. ETag: ETag,
  228. Size: ChunkSize,
  229. });
  230. onHashProgress({ loaded: FinishSize, total: FileSize });
  231. });
  232. }
  233. };
  234. // 通过和文件的 md5 对比,判断 UploadId 是否可用
  235. var isAvailableUploadList = function (PartList, callback) {
  236. var PartCount = PartList.length;
  237. // 如果没有分片,通过
  238. if (PartCount === 0) {
  239. return callback(null, true);
  240. }
  241. // 检查分片数量
  242. if (PartCount > SliceCount) {
  243. return callback(null, false);
  244. }
  245. // 检查分片大小
  246. if (PartCount > 1) {
  247. var PartSliceSize = Math.max(PartList[0].Size, PartList[1].Size);
  248. if (PartSliceSize !== SliceSize) {
  249. return callback(null, false);
  250. }
  251. }
  252. // 逐个分片计算并检查 ETag 是否一致
  253. var next = function (index) {
  254. if (index < PartCount) {
  255. var Part = PartList[index];
  256. getChunkETag(Part.PartNumber, function (err, chunk) {
  257. if (chunk && chunk.ETag === Part.ETag && chunk.Size === Part.Size) {
  258. next(index + 1);
  259. } else {
  260. callback(null, false);
  261. }
  262. });
  263. } else {
  264. callback(null, true);
  265. }
  266. };
  267. next(0);
  268. };
  269. var ep = new EventProxy();
  270. ep.on('error', function (errData) {
  271. if (!self._isRunningTask(TaskId)) return;
  272. return callback(errData);
  273. });
  274. // 存在 UploadId
  275. ep.on('upload_id_available', function (UploadData) {
  276. // 转换成 map
  277. var map = {};
  278. var list = [];
  279. util.each(UploadData.PartList, function (item) {
  280. map[item.PartNumber] = item;
  281. });
  282. for (var PartNumber = 1; PartNumber <= SliceCount; PartNumber++) {
  283. var item = map[PartNumber];
  284. if (item) {
  285. item.PartNumber = PartNumber;
  286. item.Uploaded = true;
  287. } else {
  288. item = {
  289. PartNumber: PartNumber,
  290. ETag: null,
  291. Uploaded: false,
  292. };
  293. }
  294. list.push(item);
  295. }
  296. UploadData.PartList = list;
  297. callback(null, UploadData);
  298. });
  299. // 不存在 UploadId, 初始化生成 UploadId
  300. ep.on('no_available_upload_id', function () {
  301. if (!self._isRunningTask(TaskId)) return;
  302. var _params = util.extend(
  303. {
  304. Bucket: Bucket,
  305. Region: Region,
  306. Key: Key,
  307. Headers: util.clone(params.Headers),
  308. Query: util.clone(params.Query),
  309. StorageClass: StorageClass,
  310. calledBySdk: 'sliceUploadFile',
  311. tracker: params.tracker,
  312. },
  313. params
  314. );
  315. self.multipartInit(_params, function (err, data) {
  316. if (!self._isRunningTask(TaskId)) return;
  317. if (err) return ep.emit('error', err);
  318. var UploadId = data.UploadId;
  319. if (!UploadId) {
  320. return callback({ Message: 'no upload id' });
  321. }
  322. ep.emit('upload_id_available', { UploadId: UploadId, PartList: [] });
  323. });
  324. });
  325. // 如果已存在 UploadId,找一个可以用的 UploadId
  326. ep.on('has_and_check_upload_id', function (UploadIdList) {
  327. // 串行地,找一个内容一致的 UploadId
  328. UploadIdList = UploadIdList.reverse();
  329. Async.eachLimit(
  330. UploadIdList,
  331. 1,
  332. function (UploadId, asyncCallback) {
  333. if (!self._isRunningTask(TaskId)) return;
  334. // 如果正在上传,跳过
  335. if (session.using[UploadId]) {
  336. asyncCallback(); // 检查下一个 UploadId
  337. return;
  338. }
  339. // 判断 UploadId 是否可用
  340. wholeMultipartListPart.call(
  341. self,
  342. {
  343. Bucket: Bucket,
  344. Region: Region,
  345. Key: Key,
  346. UploadId: UploadId,
  347. tracker: params.tracker,
  348. },
  349. function (err, PartListData) {
  350. if (!self._isRunningTask(TaskId)) return;
  351. if (err) {
  352. session.removeUsing(UploadId);
  353. return ep.emit('error', err);
  354. }
  355. var PartList = PartListData.PartList;
  356. PartList.forEach(function (item) {
  357. item.PartNumber *= 1;
  358. item.Size *= 1;
  359. item.ETag = item.ETag || '';
  360. });
  361. isAvailableUploadList(PartList, function (err, isAvailable) {
  362. if (!self._isRunningTask(TaskId)) return;
  363. if (err) return ep.emit('error', err);
  364. if (isAvailable) {
  365. asyncCallback({
  366. UploadId: UploadId,
  367. PartList: PartList,
  368. }); // 马上结束
  369. } else {
  370. asyncCallback(); // 检查下一个 UploadId
  371. }
  372. });
  373. }
  374. );
  375. },
  376. function (AvailableUploadData) {
  377. if (!self._isRunningTask(TaskId)) return;
  378. onHashProgress(null, true);
  379. if (AvailableUploadData && AvailableUploadData.UploadId) {
  380. ep.emit('upload_id_available', AvailableUploadData);
  381. } else {
  382. ep.emit('no_available_upload_id');
  383. }
  384. }
  385. );
  386. });
  387. // 在本地缓存找可用的 UploadId
  388. ep.on('seek_local_avail_upload_id', function (RemoteUploadIdList) {
  389. // 在本地找可用的 UploadId
  390. var uuid = session.getFileId(params.FileStat, params.ChunkSize, Bucket, Key);
  391. var LocalUploadIdList = session.getUploadIdList(uuid);
  392. if (!uuid || !LocalUploadIdList) {
  393. ep.emit('has_and_check_upload_id', RemoteUploadIdList);
  394. return;
  395. }
  396. var next = function (index) {
  397. // 如果本地找不到可用 UploadId,再一个个遍历校验远端
  398. if (index >= LocalUploadIdList.length) {
  399. ep.emit('has_and_check_upload_id', RemoteUploadIdList);
  400. return;
  401. }
  402. var UploadId = LocalUploadIdList[index];
  403. // 如果不在远端 UploadId 列表里,跳过并删除
  404. if (!util.isInArray(RemoteUploadIdList, UploadId)) {
  405. session.removeUploadId(UploadId);
  406. next(index + 1);
  407. return;
  408. }
  409. // 如果正在上传,跳过
  410. if (session.using[UploadId]) {
  411. next(index + 1);
  412. return;
  413. }
  414. // 判断 UploadId 是否存在线上
  415. wholeMultipartListPart.call(
  416. self,
  417. {
  418. Bucket: Bucket,
  419. Region: Region,
  420. Key: Key,
  421. UploadId: UploadId,
  422. tracker: params.tracker,
  423. },
  424. function (err, PartListData) {
  425. if (!self._isRunningTask(TaskId)) return;
  426. if (err) {
  427. // 如果 UploadId 获取会出错,跳过并删除
  428. session.removeUploadId(UploadId);
  429. next(index + 1);
  430. } else {
  431. // 找到可用 UploadId
  432. ep.emit('upload_id_available', {
  433. UploadId: UploadId,
  434. PartList: PartListData.PartList,
  435. });
  436. }
  437. }
  438. );
  439. };
  440. next(0);
  441. });
  442. // 获取线上 UploadId 列表
  443. ep.on('get_remote_upload_id_list', function () {
  444. // 获取符合条件的 UploadId 列表,因为同一个文件可以有多个上传任务。
  445. wholeMultipartList.call(
  446. self,
  447. {
  448. Bucket: Bucket,
  449. Region: Region,
  450. Key: Key,
  451. tracker: params.tracker,
  452. },
  453. function (err, data) {
  454. if (!self._isRunningTask(TaskId)) return;
  455. if (err) {
  456. return ep.emit('error', err);
  457. }
  458. // 整理远端 UploadId 列表
  459. var RemoteUploadIdList = util
  460. .filter(data.UploadList, function (item) {
  461. return (
  462. item.Key === Key && (!StorageClass || item.StorageClass.toUpperCase() === StorageClass.toUpperCase())
  463. );
  464. })
  465. .reverse()
  466. .map(function (item) {
  467. return item.UploadId || item.UploadID;
  468. });
  469. if (RemoteUploadIdList.length) {
  470. ep.emit('seek_local_avail_upload_id', RemoteUploadIdList);
  471. } else {
  472. // 远端没有 UploadId,清理缓存的 UploadId
  473. var uuid = session.getFileId(params.FileStat, params.ChunkSize, Bucket, Key),
  474. LocalUploadIdList;
  475. if (uuid && (LocalUploadIdList = session.getUploadIdList(uuid))) {
  476. util.each(LocalUploadIdList, function (UploadId) {
  477. session.removeUploadId(UploadId);
  478. });
  479. }
  480. ep.emit('no_available_upload_id');
  481. }
  482. }
  483. );
  484. });
  485. // 开始找可用 UploadId
  486. ep.emit('get_remote_upload_id_list');
  487. }
  488. // 获取符合条件的全部上传任务 (条件包括 Bucket, Region, Prefix)
  489. function wholeMultipartList(params, callback) {
  490. var self = this;
  491. var UploadList = [];
  492. var sendParams = {
  493. Bucket: params.Bucket,
  494. Region: params.Region,
  495. Prefix: params.Key,
  496. calledBySdk: params.calledBySdk || 'sliceUploadFile',
  497. tracker: params.tracker,
  498. };
  499. var next = function () {
  500. self.multipartList(sendParams, function (err, data) {
  501. if (err) return callback(err);
  502. UploadList.push.apply(UploadList, data.Upload || []);
  503. if (data.IsTruncated === 'true') {
  504. // 列表不完整
  505. sendParams.KeyMarker = data.NextKeyMarker;
  506. sendParams.UploadIdMarker = data.NextUploadIdMarker;
  507. next();
  508. } else {
  509. callback(null, { UploadList: UploadList });
  510. }
  511. });
  512. };
  513. next();
  514. }
  515. // 获取指定上传任务的分块列表
  516. function wholeMultipartListPart(params, callback) {
  517. var self = this;
  518. var PartList = [];
  519. var sendParams = {
  520. Bucket: params.Bucket,
  521. Region: params.Region,
  522. Key: params.Key,
  523. UploadId: params.UploadId,
  524. calledBySdk: 'sliceUploadFile',
  525. tracker: params.tracker,
  526. };
  527. var next = function () {
  528. self.multipartListPart(sendParams, function (err, data) {
  529. if (err) return callback(err);
  530. PartList.push.apply(PartList, data.Part || []);
  531. if (data.IsTruncated === 'true') {
  532. // 列表不完整
  533. sendParams.PartNumberMarker = data.NextPartNumberMarker;
  534. next();
  535. } else {
  536. callback(null, { PartList: PartList });
  537. }
  538. });
  539. };
  540. next();
  541. }
  542. // 上传文件分块,包括
  543. /*
  544. UploadId (上传任务编号)
  545. AsyncLimit (并发量),
  546. SliceList (上传的分块数组),
  547. FilePath (本地文件的位置),
  548. SliceSize (文件分块大小)
  549. FileSize (文件大小)
  550. onProgress (上传成功之后的回调函数)
  551. */
  552. function uploadSliceList(params, cb) {
  553. var self = this;
  554. var TaskId = params.TaskId;
  555. var Bucket = params.Bucket;
  556. var Region = params.Region;
  557. var Key = params.Key;
  558. var UploadData = params.UploadData;
  559. var FileSize = params.FileSize;
  560. var SliceSize = params.SliceSize;
  561. var ChunkParallel = Math.min(params.AsyncLimit || self.options.ChunkParallelLimit || 1, 256);
  562. var FilePath = params.FilePath;
  563. var SliceCount = Math.ceil(FileSize / SliceSize);
  564. var FinishSize = 0;
  565. var ServerSideEncryption = params.ServerSideEncryption;
  566. var needUploadSlices = util.filter(UploadData.PartList, function (SliceItem) {
  567. if (SliceItem['Uploaded']) {
  568. FinishSize += SliceItem['PartNumber'] >= SliceCount ? FileSize % SliceSize || SliceSize : SliceSize;
  569. }
  570. return !SliceItem['Uploaded'];
  571. });
  572. var onProgress = params.onProgress;
  573. Async.eachLimit(
  574. needUploadSlices,
  575. ChunkParallel,
  576. function (SliceItem, asyncCallback) {
  577. if (!self._isRunningTask(TaskId)) return;
  578. var PartNumber = SliceItem['PartNumber'];
  579. var currentSize =
  580. Math.min(FileSize, SliceItem['PartNumber'] * SliceSize) - (SliceItem['PartNumber'] - 1) * SliceSize;
  581. var preAddSize = 0;
  582. uploadSliceItem.call(
  583. self,
  584. {
  585. TaskId: TaskId,
  586. Bucket: Bucket,
  587. Region: Region,
  588. Key: Key,
  589. SliceSize: SliceSize,
  590. FileSize: FileSize,
  591. PartNumber: PartNumber,
  592. ServerSideEncryption: ServerSideEncryption,
  593. FilePath: FilePath,
  594. UploadData: UploadData,
  595. onProgress: function (data) {
  596. FinishSize += data.loaded - preAddSize;
  597. preAddSize = data.loaded;
  598. onProgress({ loaded: FinishSize, total: FileSize });
  599. },
  600. tracker: params.tracker,
  601. },
  602. function (err, data) {
  603. if (!self._isRunningTask(TaskId)) return;
  604. if (err) {
  605. FinishSize -= preAddSize;
  606. } else {
  607. FinishSize += currentSize - preAddSize;
  608. SliceItem.ETag = data.ETag;
  609. }
  610. onProgress({ loaded: FinishSize, total: FileSize });
  611. asyncCallback(err || null, data);
  612. }
  613. );
  614. },
  615. function (err) {
  616. if (!self._isRunningTask(TaskId)) return;
  617. if (err) return cb(err);
  618. cb(null, {
  619. UploadId: UploadData.UploadId,
  620. SliceList: UploadData.PartList,
  621. });
  622. }
  623. );
  624. }
  625. // 上传指定分片
  626. function uploadSliceItem(params, callback) {
  627. var self = this;
  628. var TaskId = params.TaskId;
  629. var Bucket = params.Bucket;
  630. var Region = params.Region;
  631. var Key = params.Key;
  632. var FileSize = params.FileSize;
  633. var FilePath = params.FilePath;
  634. var PartNumber = params.PartNumber * 1;
  635. var SliceSize = params.SliceSize;
  636. var ServerSideEncryption = params.ServerSideEncryption;
  637. var UploadData = params.UploadData;
  638. var ChunkRetryTimes = self.options.ChunkRetryTimes + 1;
  639. var Headers = params.Headers || {};
  640. var start = SliceSize * (PartNumber - 1);
  641. var ContentLength = SliceSize;
  642. var end = start + SliceSize;
  643. if (end > FileSize) {
  644. end = FileSize;
  645. ContentLength = end - start;
  646. }
  647. var headersWhiteList = ['x-cos-traffic-limit', 'x-cos-mime-limit'];
  648. var headers = {};
  649. util.each(Headers, function (v, k) {
  650. if (headersWhiteList.indexOf(k) > -1) {
  651. headers[k] = v;
  652. }
  653. });
  654. util.fileSlice(FilePath, start, end, function (Body) {
  655. var md5 = util.getFileMd5(Body);
  656. var contentMd5 = md5 ? util.binaryBase64(md5) : null;
  657. var PartItem = UploadData.PartList[PartNumber - 1];
  658. Async.retry(
  659. ChunkRetryTimes,
  660. function (tryCallback) {
  661. if (!self._isRunningTask(TaskId)) return;
  662. self.multipartUpload(
  663. {
  664. TaskId: TaskId,
  665. Bucket: Bucket,
  666. Region: Region,
  667. Key: Key,
  668. ContentLength: ContentLength,
  669. PartNumber: PartNumber,
  670. UploadId: UploadData.UploadId,
  671. ServerSideEncryption: ServerSideEncryption,
  672. Body: Body,
  673. Headers: headers,
  674. onProgress: params.onProgress,
  675. ContentMD5: contentMd5,
  676. calledBySdk: 'sliceUploadFile',
  677. tracker: params.tracker,
  678. },
  679. function (err, data) {
  680. if (!self._isRunningTask(TaskId)) return;
  681. if (err) {
  682. return tryCallback(err);
  683. } else {
  684. PartItem.Uploaded = true;
  685. return tryCallback(null, data);
  686. }
  687. }
  688. );
  689. },
  690. function (err, data) {
  691. if (!self._isRunningTask(TaskId)) return;
  692. return callback(err, data);
  693. }
  694. );
  695. });
  696. }
  697. // 完成分块上传
  698. function uploadSliceComplete(params, callback) {
  699. var Bucket = params.Bucket;
  700. var Region = params.Region;
  701. var Key = params.Key;
  702. var UploadId = params.UploadId;
  703. var SliceList = params.SliceList;
  704. var self = this;
  705. var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
  706. var Parts = SliceList.map(function (item) {
  707. return {
  708. PartNumber: item.PartNumber,
  709. ETag: item.ETag,
  710. };
  711. });
  712. // 完成上传的请求也做重试
  713. Async.retry(
  714. ChunkRetryTimes,
  715. function (tryCallback) {
  716. self.multipartComplete(
  717. {
  718. Bucket: Bucket,
  719. Region: Region,
  720. Key: Key,
  721. UploadId: UploadId,
  722. Parts: Parts,
  723. calledBySdk: 'sliceUploadFile',
  724. Headers: params.Headers || {},
  725. tracker: params.tracker,
  726. },
  727. tryCallback
  728. );
  729. },
  730. function (err, data) {
  731. callback(err, data);
  732. }
  733. );
  734. }
  735. // 抛弃分块上传任务
  736. /*
  737. AsyncLimit (抛弃上传任务的并发量),
  738. UploadId (上传任务的编号,当 Level 为 task 时候需要)
  739. Level (抛弃分块上传任务的级别,task : 抛弃指定的上传任务,file : 抛弃指定的文件对应的上传任务,其他值 :抛弃指定Bucket 的全部上传任务)
  740. */
  741. function abortUploadTask(params, callback) {
  742. var Bucket = params.Bucket;
  743. var Region = params.Region;
  744. var Key = params.Key;
  745. var UploadId = params.UploadId;
  746. var Level = params.Level || 'task';
  747. var AsyncLimit = params.AsyncLimit;
  748. var self = this;
  749. var ep = new EventProxy();
  750. ep.on('error', function (errData) {
  751. return callback(errData);
  752. });
  753. // 已经获取到需要抛弃的任务列表
  754. ep.on('get_abort_array', function (AbortArray) {
  755. abortUploadTaskArray.call(
  756. self,
  757. {
  758. Bucket: Bucket,
  759. Region: Region,
  760. Key: Key,
  761. Headers: params.Headers,
  762. AsyncLimit: AsyncLimit,
  763. AbortArray: AbortArray,
  764. },
  765. function (err, data) {
  766. if (err) {
  767. return callback(err);
  768. }
  769. callback(null, data);
  770. }
  771. );
  772. });
  773. if (Level === 'bucket') {
  774. // Bucket 级别的任务抛弃,抛弃该 Bucket 下的全部上传任务
  775. wholeMultipartList.call(
  776. self,
  777. {
  778. Bucket: Bucket,
  779. Region: Region,
  780. calledBySdk: 'abortUploadTask',
  781. },
  782. function (err, data) {
  783. if (err) {
  784. return callback(err);
  785. }
  786. ep.emit('get_abort_array', data.UploadList || []);
  787. }
  788. );
  789. } else if (Level === 'file') {
  790. // 文件级别的任务抛弃,抛弃该文件的全部上传任务
  791. if (!Key) return callback({ error: 'abort_upload_task_no_key' });
  792. wholeMultipartList.call(
  793. self,
  794. {
  795. Bucket: Bucket,
  796. Region: Region,
  797. Key: Key,
  798. calledBySdk: 'abortUploadTask',
  799. },
  800. function (err, data) {
  801. if (err) {
  802. return callback(err);
  803. }
  804. ep.emit('get_abort_array', data.UploadList || []);
  805. }
  806. );
  807. } else if (Level === 'task') {
  808. // 单个任务级别的任务抛弃,抛弃指定 UploadId 的上传任务
  809. if (!UploadId) return callback({ error: 'abort_upload_task_no_id' });
  810. if (!Key) return callback({ error: 'abort_upload_task_no_key' });
  811. ep.emit('get_abort_array', [
  812. {
  813. Key: Key,
  814. UploadId: UploadId,
  815. },
  816. ]);
  817. } else {
  818. return callback({ error: 'abort_unknown_level' });
  819. }
  820. }
  821. // 批量抛弃分块上传任务
  822. function abortUploadTaskArray(params, callback) {
  823. var Bucket = params.Bucket;
  824. var Region = params.Region;
  825. var Key = params.Key;
  826. var AbortArray = params.AbortArray;
  827. var AsyncLimit = params.AsyncLimit || 1;
  828. var self = this;
  829. var index = 0;
  830. var resultList = new Array(AbortArray.length);
  831. Async.eachLimit(
  832. AbortArray,
  833. AsyncLimit,
  834. function (AbortItem, callback) {
  835. var eachIndex = index;
  836. if (Key && Key !== AbortItem.Key) {
  837. resultList[eachIndex] = { error: { KeyNotMatch: true } };
  838. callback(null);
  839. return;
  840. }
  841. var UploadId = AbortItem.UploadId || AbortItem.UploadID;
  842. self.multipartAbort(
  843. {
  844. Bucket: Bucket,
  845. Region: Region,
  846. Key: AbortItem.Key,
  847. Headers: params.Headers,
  848. UploadId: UploadId,
  849. },
  850. function (err) {
  851. var task = {
  852. Bucket: Bucket,
  853. Region: Region,
  854. Key: AbortItem.Key,
  855. UploadId: UploadId,
  856. };
  857. resultList[eachIndex] = { error: err, task: task };
  858. callback(null);
  859. }
  860. );
  861. index++;
  862. },
  863. function (err) {
  864. if (err) {
  865. return callback(err);
  866. }
  867. var successList = [];
  868. var errorList = [];
  869. for (var i = 0, len = resultList.length; i < len; i++) {
  870. var item = resultList[i];
  871. if (item['task']) {
  872. if (item['error']) {
  873. errorList.push(item['task']);
  874. } else {
  875. successList.push(item['task']);
  876. }
  877. }
  878. }
  879. return callback(null, {
  880. successList: successList,
  881. errorList: errorList,
  882. });
  883. }
  884. );
  885. }
  886. // 高级上传
  887. async function uploadFile(params, callback) {
  888. var self = this;
  889. // 判断多大的文件使用分片上传
  890. var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
  891. var taskList = [];
  892. // var FileSize = params.FileSize;
  893. var FileSize;
  894. try {
  895. FileSize = await util.getFileSizeByPath(params.FilePath);
  896. } catch (e) {
  897. callback({ error: e });
  898. return;
  899. }
  900. var fileInfo = { TaskId: '' };
  901. // 上传链路
  902. if (self.options.EnableReporter) {
  903. const accelerate =
  904. self.options.UseAccelerate ||
  905. (typeof self.options.Domain === 'string' && self.options.Domain.includes('accelerate.'));
  906. const realApi = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
  907. params.tracker = new Tracker({
  908. Beacon: self.options.BeaconReporter,
  909. clsReporter: self.options.ClsReporter,
  910. bucket: params.Bucket,
  911. region: params.Region,
  912. apiName: 'uploadFile',
  913. realApi,
  914. fileKey: params.Key,
  915. fileSize: FileSize,
  916. accelerate,
  917. deepTracker: self.options.DeepTracker,
  918. customId: self.options.CustomId,
  919. delay: self.options.TrackerDelay,
  920. });
  921. }
  922. // 整理 option,用于返回给回调
  923. util.each(params, function (v, k) {
  924. if (typeof v !== 'object' && typeof v !== 'function') {
  925. fileInfo[k] = v;
  926. }
  927. });
  928. // 处理文件 TaskReady
  929. var _onTaskReady = params.onTaskReady;
  930. params.onTaskReady = function (tid) {
  931. fileInfo.TaskId = tid;
  932. _onTaskReady && _onTaskReady(tid);
  933. };
  934. // 处理文件完成
  935. var _onFileFinish = params.onFileFinish;
  936. var onFileFinish = function (err, data) {
  937. // 格式化上报参数并上报
  938. params.tracker && params.tracker.report(err, data);
  939. _onFileFinish && _onFileFinish(err, data, fileInfo);
  940. callback && callback(err, data);
  941. };
  942. // 添加上传任务
  943. var simpleUploadMethod = self.options.SimpleUploadMethod === 'postObject' ? 'postObject' : 'putObject';
  944. var api = FileSize > SliceSize ? 'sliceUploadFile' : simpleUploadMethod;
  945. taskList.push({
  946. api: api,
  947. params: params,
  948. callback: onFileFinish,
  949. });
  950. self._addTasks(taskList);
  951. }
  952. // 批量上传文件
  953. async function uploadFiles(params, callback) {
  954. var self = this;
  955. // 判断多大的文件使用分片上传
  956. var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
  957. // 汇总返回进度
  958. var TotalSize = 0;
  959. var TotalFinish = 0;
  960. var onTotalProgress = util.throttleOnProgress.call(self, TotalFinish, params.onProgress);
  961. // 汇总返回回调
  962. var unFinishCount = params.files.length;
  963. var _onTotalFileFinish = params.onFileFinish;
  964. var resultList = Array(unFinishCount);
  965. var onTotalFileFinish = function (err, data, options) {
  966. onTotalProgress(null, true);
  967. _onTotalFileFinish && _onTotalFileFinish(err, data, options);
  968. resultList[options.Index] = {
  969. options: options,
  970. error: err,
  971. data: data,
  972. };
  973. if (--unFinishCount <= 0 && callback) {
  974. callback(null, {
  975. files: resultList,
  976. });
  977. }
  978. };
  979. // 开始处理每个文件
  980. var taskList = [];
  981. var getTaskList = function () {
  982. return params.files.map(function (fileParams, index) {
  983. return new Promise(async function (resolve) {
  984. var FileSize = 0;
  985. try {
  986. FileSize = await util.getFileSizeByPath(fileParams.FilePath);
  987. } catch (e) {
  988. // 获取大小出错不做处理,不会加入队列
  989. }
  990. var fileInfo = { Index: index, TaskId: '' };
  991. // 更新文件总大小
  992. TotalSize += FileSize;
  993. // 单个文件上传链路
  994. if (self.options.EnableReporter) {
  995. const accelerate =
  996. self.options.UseAccelerate ||
  997. (typeof self.options.Domain === 'string' && self.options.Domain.includes('accelerate.'));
  998. const realApi = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
  999. fileParams.tracker = new Tracker({
  1000. Beacon: self.options.BeaconReporter,
  1001. clsReporter: self.options.ClsReporter,
  1002. bucket: fileParams.Bucket,
  1003. region: fileParams.Region,
  1004. apiName: 'uploadFiles',
  1005. realApi,
  1006. fileKey: fileParams.Key,
  1007. fileSize: FileSize,
  1008. accelerate,
  1009. deepTracker: self.options.DeepTracker,
  1010. customId: self.options.CustomId,
  1011. delay: self.options.TrackerDelay,
  1012. });
  1013. }
  1014. // 整理 option,用于返回给回调
  1015. util.each(fileParams, function (v, k) {
  1016. if (typeof v !== 'object' && typeof v !== 'function') {
  1017. fileInfo[k] = v;
  1018. }
  1019. });
  1020. // 处理单个文件 TaskReady
  1021. var _onTaskReady = fileParams.onTaskReady;
  1022. fileParams.onTaskReady = function (tid) {
  1023. fileInfo.TaskId = tid;
  1024. _onTaskReady && _onTaskReady(tid);
  1025. };
  1026. // 处理单个文件进度
  1027. var PreAddSize = 0;
  1028. var _onProgress = fileParams.onProgress;
  1029. fileParams.onProgress = function (info) {
  1030. TotalFinish = TotalFinish - PreAddSize + info.loaded;
  1031. PreAddSize = info.loaded;
  1032. _onProgress && _onProgress(info);
  1033. onTotalProgress({ loaded: TotalFinish, total: TotalSize });
  1034. };
  1035. // 处理单个文件完成
  1036. var _onFileFinish = fileParams.onFileFinish;
  1037. var onFileFinish = function (err, data) {
  1038. // 格式化上报参数并上报
  1039. fileParams.tracker && fileParams.tracker.report(err, data);
  1040. _onFileFinish && _onFileFinish(err, data);
  1041. onTotalFileFinish && onTotalFileFinish(err, data, fileInfo);
  1042. };
  1043. // 添加上传任务
  1044. var simpleUploadMethod = self.options.SimpleUploadMethod === 'postObject' ? 'postObject' : 'putObject';
  1045. var api = FileSize > SliceSize ? 'sliceUploadFile' : simpleUploadMethod;
  1046. taskList.push({
  1047. api: api,
  1048. params: fileParams,
  1049. callback: onFileFinish,
  1050. });
  1051. resolve(true);
  1052. });
  1053. });
  1054. };
  1055. await Promise.all(getTaskList());
  1056. self._addTasks(taskList);
  1057. }
  1058. // 分片复制文件
  1059. function sliceCopyFile(params, callback) {
  1060. var ep = new EventProxy();
  1061. var self = this;
  1062. var Bucket = params.Bucket;
  1063. var Region = params.Region;
  1064. var Key = params.Key;
  1065. var CopySource = params.CopySource;
  1066. var m = util.getSourceParams.call(this, CopySource);
  1067. if (!m) {
  1068. callback({ error: 'CopySource format error' });
  1069. return;
  1070. }
  1071. var SourceBucket = m.Bucket;
  1072. var SourceRegion = m.Region;
  1073. var SourceKey = decodeURIComponent(m.Key);
  1074. var CopySliceSize = params.CopySliceSize === undefined ? self.options.CopySliceSize : params.CopySliceSize;
  1075. CopySliceSize = Math.max(0, CopySliceSize);
  1076. var ChunkSize = params.CopyChunkSize || this.options.CopyChunkSize;
  1077. var ChunkParallel = this.options.CopyChunkParallelLimit;
  1078. var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
  1079. var ChunkCount = 0;
  1080. var FinishSize = 0;
  1081. var FileSize;
  1082. var onProgress;
  1083. var SourceResHeaders = {};
  1084. var SourceHeaders = {};
  1085. var TargetHeader = {};
  1086. // 分片复制完成,开始 multipartComplete 操作
  1087. ep.on('copy_slice_complete', function (UploadData) {
  1088. var metaHeaders = {};
  1089. util.each(params.Headers, function (val, k) {
  1090. if (k.toLowerCase().indexOf('x-cos-meta-') === 0) metaHeaders[k] = val;
  1091. });
  1092. var Parts = util.map(UploadData.PartList, function (item) {
  1093. return {
  1094. PartNumber: item.PartNumber,
  1095. ETag: item.ETag,
  1096. };
  1097. });
  1098. // 完成上传的请求也做重试
  1099. Async.retry(
  1100. ChunkRetryTimes,
  1101. function (tryCallback) {
  1102. self.multipartComplete(
  1103. {
  1104. Bucket: Bucket,
  1105. Region: Region,
  1106. Key: Key,
  1107. UploadId: UploadData.UploadId,
  1108. Parts: Parts,
  1109. tracker: params.tracker,
  1110. calledBySdk: 'sliceCopyFile',
  1111. },
  1112. tryCallback
  1113. );
  1114. },
  1115. function (err, data) {
  1116. session.removeUsing(UploadData.UploadId); // 标记 UploadId 没被使用了,因为复制没提供重试,所以只要出错,就是 UploadId 停用了。
  1117. if (err) {
  1118. onProgress(null, true);
  1119. return callback(err);
  1120. }
  1121. session.removeUploadId(UploadData.UploadId);
  1122. onProgress({ loaded: FileSize, total: FileSize }, true);
  1123. callback(null, data);
  1124. }
  1125. );
  1126. });
  1127. ep.on('get_copy_data_finish', function (UploadData) {
  1128. // 处理 UploadId 缓存
  1129. var uuid = session.getCopyFileId(CopySource, SourceResHeaders, ChunkSize, Bucket, Key);
  1130. uuid && session.saveUploadId(uuid, UploadData.UploadId, self.options.UploadIdCacheLimit); // 缓存 UploadId
  1131. session.setUsing(UploadData.UploadId); // 标记 UploadId 为正在使用
  1132. var needCopySlices = util.filter(UploadData.PartList, function (SliceItem) {
  1133. if (SliceItem['Uploaded']) {
  1134. FinishSize += SliceItem['PartNumber'] >= ChunkCount ? FileSize % ChunkSize || ChunkSize : ChunkSize;
  1135. }
  1136. return !SliceItem['Uploaded'];
  1137. });
  1138. Async.eachLimit(
  1139. needCopySlices,
  1140. ChunkParallel,
  1141. function (SliceItem, asyncCallback) {
  1142. var PartNumber = SliceItem.PartNumber;
  1143. var CopySourceRange = SliceItem.CopySourceRange;
  1144. var currentSize = SliceItem.end - SliceItem.start;
  1145. var preAddSize = 0;
  1146. Async.retry(
  1147. ChunkRetryTimes,
  1148. function (tryCallback) {
  1149. copySliceItem.call(
  1150. self,
  1151. {
  1152. Bucket: Bucket,
  1153. Region: Region,
  1154. Key: Key,
  1155. CopySource: CopySource,
  1156. UploadId: UploadData.UploadId,
  1157. PartNumber: PartNumber,
  1158. CopySourceRange: CopySourceRange,
  1159. tracker: params.tracker,
  1160. calledBySdk: 'sliceCopyFile',
  1161. onProgress: function (data) {
  1162. FinishSize += data.loaded - preAddSize;
  1163. preAddSize = data.loaded;
  1164. onProgress({ loaded: FinishSize, total: FileSize });
  1165. },
  1166. },
  1167. tryCallback
  1168. );
  1169. },
  1170. function (err, data) {
  1171. if (err) {
  1172. return asyncCallback(err);
  1173. }
  1174. onProgress({ loaded: FinishSize, total: FileSize });
  1175. FinishSize += currentSize - preAddSize;
  1176. SliceItem.ETag = data.ETag;
  1177. asyncCallback(err || null, data);
  1178. }
  1179. );
  1180. },
  1181. function (err) {
  1182. if (err) {
  1183. session.removeUsing(UploadData.UploadId); // 标记 UploadId 没被使用了,因为复制没提供重试,所以只要出错,就是 UploadId 停用了。
  1184. onProgress(null, true);
  1185. return callback(err);
  1186. }
  1187. ep.emit('copy_slice_complete', UploadData);
  1188. }
  1189. );
  1190. });
  1191. ep.on('get_chunk_size_finish', function () {
  1192. var createNewUploadId = function () {
  1193. self.multipartInit(
  1194. {
  1195. Bucket: Bucket,
  1196. Region: Region,
  1197. Key: Key,
  1198. Headers: TargetHeader,
  1199. tracker: params.tracker,
  1200. calledBySdk: 'sliceCopyFile',
  1201. },
  1202. function (err, data) {
  1203. if (err) return callback(err);
  1204. params.UploadId = data.UploadId;
  1205. ep.emit('get_copy_data_finish', { UploadId: params.UploadId, PartList: params.PartList });
  1206. }
  1207. );
  1208. };
  1209. // 在本地找可用的 UploadId
  1210. var uuid = session.getCopyFileId(CopySource, SourceResHeaders, ChunkSize, Bucket, Key);
  1211. var LocalUploadIdList = session.getUploadIdList(uuid);
  1212. if (!uuid || !LocalUploadIdList) return createNewUploadId();
  1213. var next = function (index) {
  1214. // 如果本地找不到可用 UploadId,再一个个遍历校验远端
  1215. if (index >= LocalUploadIdList.length) return createNewUploadId();
  1216. var UploadId = LocalUploadIdList[index];
  1217. // 如果正在被使用,跳过
  1218. if (session.using[UploadId]) return next(index + 1);
  1219. // 判断 UploadId 是否存在线上
  1220. wholeMultipartListPart.call(
  1221. self,
  1222. {
  1223. Bucket: Bucket,
  1224. Region: Region,
  1225. Key: Key,
  1226. UploadId: UploadId,
  1227. tracker: params.tracker,
  1228. calledBySdk: 'sliceCopyFile',
  1229. },
  1230. function (err, PartListData) {
  1231. if (err) {
  1232. // 如果 UploadId 获取会出错,跳过并删除
  1233. session.removeUploadId(UploadId);
  1234. next(index + 1);
  1235. } else {
  1236. // 如果异步回来 UploadId 已经被用了,也跳过
  1237. if (session.using[UploadId]) return next(index + 1);
  1238. // 找到可用 UploadId
  1239. var finishETagMap = {};
  1240. var offset = 0;
  1241. util.each(PartListData.PartList, function (PartItem) {
  1242. var size = parseInt(PartItem.Size);
  1243. var end = offset + size - 1;
  1244. finishETagMap[PartItem.PartNumber + '|' + offset + '|' + end] = PartItem.ETag;
  1245. offset += size;
  1246. });
  1247. util.each(params.PartList, function (PartItem) {
  1248. var ETag = finishETagMap[PartItem.PartNumber + '|' + PartItem.start + '|' + PartItem.end];
  1249. if (ETag) {
  1250. PartItem.ETag = ETag;
  1251. PartItem.Uploaded = true;
  1252. }
  1253. });
  1254. ep.emit('get_copy_data_finish', { UploadId: UploadId, PartList: params.PartList });
  1255. }
  1256. }
  1257. );
  1258. };
  1259. next(0);
  1260. });
  1261. ep.on('get_file_size_finish', function () {
  1262. // 控制分片大小
  1263. (function () {
  1264. var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
  1265. var AutoChunkSize = 1024 * 1024;
  1266. for (var i = 0; i < SIZE.length; i++) {
  1267. AutoChunkSize = SIZE[i] * 1024 * 1024;
  1268. if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
  1269. }
  1270. params.ChunkSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
  1271. ChunkCount = Math.ceil(FileSize / ChunkSize);
  1272. var list = [];
  1273. for (var partNumber = 1; partNumber <= ChunkCount; partNumber++) {
  1274. var start = (partNumber - 1) * ChunkSize;
  1275. var end = partNumber * ChunkSize < FileSize ? partNumber * ChunkSize - 1 : FileSize - 1;
  1276. var item = {
  1277. PartNumber: partNumber,
  1278. start: start,
  1279. end: end,
  1280. CopySourceRange: 'bytes=' + start + '-' + end,
  1281. };
  1282. list.push(item);
  1283. }
  1284. params.PartList = list;
  1285. })();
  1286. var TargetHeader;
  1287. if (params.Headers['x-cos-metadata-directive'] === 'Replaced') {
  1288. TargetHeader = params.Headers;
  1289. } else {
  1290. TargetHeader = SourceHeaders;
  1291. }
  1292. TargetHeader['x-cos-storage-class'] = params.Headers['x-cos-storage-class'] || SourceHeaders['x-cos-storage-class'];
  1293. TargetHeader = util.clearKey(TargetHeader);
  1294. /**
  1295. * 对于归档存储的对象,如果未恢复副本,则不允许 Copy
  1296. */
  1297. if (SourceHeaders['x-cos-storage-class'] === 'ARCHIVE' || SourceHeaders['x-cos-storage-class'] === 'DEEP_ARCHIVE') {
  1298. var restoreHeader = SourceHeaders['x-cos-restore'];
  1299. if (!restoreHeader || restoreHeader === 'ongoing-request="true"') {
  1300. callback({ error: 'Unrestored archive object is not allowed to be copied' });
  1301. return;
  1302. }
  1303. }
  1304. /**
  1305. * 去除一些无用的头部,规避 multipartInit 出错
  1306. * 这些头部通常是在 putObjectCopy 时才使用
  1307. */
  1308. delete TargetHeader['x-cos-copy-source'];
  1309. delete TargetHeader['x-cos-metadata-directive'];
  1310. delete TargetHeader['x-cos-copy-source-If-Modified-Since'];
  1311. delete TargetHeader['x-cos-copy-source-If-Unmodified-Since'];
  1312. delete TargetHeader['x-cos-copy-source-If-Match'];
  1313. delete TargetHeader['x-cos-copy-source-If-None-Match'];
  1314. ep.emit('get_chunk_size_finish');
  1315. });
  1316. // 获取远端复制源文件的大小
  1317. self.headObject(
  1318. {
  1319. Bucket: SourceBucket,
  1320. Region: SourceRegion,
  1321. Key: SourceKey,
  1322. tracker: params.tracker,
  1323. calledBySdk: 'sliceCopyFile',
  1324. },
  1325. function (err, data) {
  1326. if (err) {
  1327. if (err.statusCode && err.statusCode === 404) {
  1328. callback({ ErrorStatus: SourceKey + ' Not Exist' });
  1329. } else {
  1330. callback(err);
  1331. }
  1332. return;
  1333. }
  1334. FileSize = params.FileSize = data.headers['content-length'];
  1335. if (FileSize === undefined || !FileSize) {
  1336. callback({ error: 'get Content-Length error, please add "Content-Length" to CORS ExposeHeader setting.' });
  1337. return;
  1338. }
  1339. params.tracker && params.tracker.setParams({ httpSize: FileSize });
  1340. onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
  1341. // 开始上传
  1342. if (FileSize <= CopySliceSize) {
  1343. if (!params.Headers['x-cos-metadata-directive']) {
  1344. params.Headers['x-cos-metadata-directive'] = 'Copy';
  1345. }
  1346. self.putObjectCopy(Object.assign(params, { calledBySdk: 'sliceCopyFile' }), function (err, data) {
  1347. if (err) {
  1348. onProgress(null, true);
  1349. return callback(err);
  1350. }
  1351. onProgress({ loaded: FileSize, total: FileSize }, true);
  1352. callback(err, data);
  1353. });
  1354. } else {
  1355. var resHeaders = data.headers;
  1356. SourceResHeaders = resHeaders;
  1357. SourceHeaders = {
  1358. 'Cache-Control': resHeaders['cache-control'],
  1359. 'Content-Disposition': resHeaders['content-disposition'],
  1360. 'Content-Encoding': resHeaders['content-encoding'],
  1361. 'Content-Type': resHeaders['content-type'],
  1362. Expires: resHeaders['expires'],
  1363. 'x-cos-storage-class': resHeaders['x-cos-storage-class'],
  1364. };
  1365. util.each(resHeaders, function (v, k) {
  1366. var metaPrefix = 'x-cos-meta-';
  1367. if (k.indexOf(metaPrefix) === 0 && k.length > metaPrefix.length) {
  1368. SourceHeaders[k] = v;
  1369. }
  1370. });
  1371. ep.emit('get_file_size_finish');
  1372. }
  1373. }
  1374. );
  1375. }
  1376. // 复制指定分片
  1377. function copySliceItem(params, callback) {
  1378. var TaskId = params.TaskId;
  1379. var Bucket = params.Bucket;
  1380. var Region = params.Region;
  1381. var Key = params.Key;
  1382. var CopySource = params.CopySource;
  1383. var UploadId = params.UploadId;
  1384. var PartNumber = params.PartNumber * 1;
  1385. var CopySourceRange = params.CopySourceRange;
  1386. var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
  1387. var self = this;
  1388. Async.retry(
  1389. ChunkRetryTimes,
  1390. function (tryCallback) {
  1391. self.uploadPartCopy(
  1392. {
  1393. TaskId: TaskId,
  1394. Bucket: Bucket,
  1395. Region: Region,
  1396. Key: Key,
  1397. CopySource: CopySource,
  1398. UploadId: UploadId,
  1399. PartNumber: PartNumber,
  1400. CopySourceRange: CopySourceRange,
  1401. onProgress: params.onProgress,
  1402. tracker: params.tracker,
  1403. calledBySdk: params.calledBySdk,
  1404. },
  1405. function (err, data) {
  1406. tryCallback(err || null, data);
  1407. }
  1408. );
  1409. },
  1410. function (err, data) {
  1411. return callback(err, data);
  1412. }
  1413. );
  1414. }
  1415. var API_MAP = {
  1416. sliceUploadFile: sliceUploadFile,
  1417. abortUploadTask: abortUploadTask,
  1418. uploadFile: uploadFile,
  1419. uploadFiles: uploadFiles,
  1420. sliceCopyFile: sliceCopyFile,
  1421. };
  1422. module.exports.init = function (COS, task) {
  1423. task.transferToTaskMethod(API_MAP, 'sliceUploadFile');
  1424. util.each(API_MAP, function (fn, apiName) {
  1425. COS.prototype[apiName] = util.apiWrapper(apiName, fn);
  1426. });
  1427. };