slice-task.html 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>自定义的分片上传</title>
  6. <style>
  7. h1, h2 {
  8. font-weight: normal;
  9. }
  10. #msg {
  11. margin-top: 10px;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <h1>自定义的分片上传</h1>
  17. <input id="fileSelector" type="file">
  18. <input id="submitBtn" type="button" value="上传文件">
  19. <input id="cancelBtn" type="button" value="取消最后一个上传文件">
  20. <div id="msg"></div>
  21. <script src="../dist/cos-js-sdk-v5.js"></script>
  22. <script src="./common/async.js"></script>
  23. <script>
  24. (function () {
  25. // 请求用到的参数
  26. var Bucket = 'test-1250000000';
  27. var Region = 'ap-guangzhou';
  28. var ChunkSize = 1024 * 1024 * 8;
  29. // 初始化 SDK
  30. var cos = new COS({
  31. getAuthorization: function (options, callback) {
  32. var url = '/sts'; // 如果是 npm run sts.js 起的 nodejs server,使用这个
  33. var xhr = new XMLHttpRequest();
  34. xhr.open('GET', url, true);
  35. xhr.onload = function (e) {
  36. try {
  37. var data = JSON.parse(e.target.responseText);
  38. var credentials = data.credentials;
  39. } catch (e) {
  40. }
  41. if (!data || !credentials) return console.error('credentials invalid');
  42. callback({
  43. TmpSecretId: credentials.tmpSecretId,
  44. TmpSecretKey: credentials.tmpSecretKey,
  45. SecurityToken: credentials.sessionToken,
  46. StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
  47. ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900
  48. });
  49. };
  50. xhr.send();
  51. },
  52. });
  53. var uuid = function () {
  54. var S4 = function () {
  55. return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  56. };
  57. return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
  58. };
  59. // 外部维护上传任务状态
  60. var lastTaskId;
  61. var taskStateMap = {};
  62. cos._isRunningTask = function (tid) {
  63. return taskStateMap[tid] === 'uploading';
  64. };
  65. // 上传文件
  66. var uploadFile = function (file, callback) {
  67. var TaskId = lastTaskId = uuid();
  68. taskStateMap[TaskId] = 'uploading';
  69. var fileSize = file.size;
  70. var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
  71. console.log('上传任务已开始:' + lastTaskId, TaskId, Key);
  72. // 创建 UploadId
  73. cos.multipartInit({
  74. Bucket: Bucket,
  75. Region: Region,
  76. Key: Key,
  77. }, function (err, data) {
  78. if (!cos._isRunningTask(TaskId)) return;
  79. if (err) {
  80. taskStateMap[TaskId] = 'error';
  81. return console.error('UploadId 创建出错:', err);
  82. }
  83. var UploadId = data.UploadId;
  84. console.log('UploadId 已创建:', UploadId);
  85. var Parts = new Array(Math.ceil(fileSize / ChunkSize)).fill(0).map(function (item, index) {
  86. return {PartNumber: index + 1};
  87. });
  88. Async.eachLimit(Parts, 3, function (partItem, nextPart) {
  89. if (!cos._isRunningTask(TaskId)) return;
  90. var PartNumber = partItem.PartNumber;
  91. var start = (PartNumber - 1) * ChunkSize;
  92. var end = Math.min(start + ChunkSize);
  93. var blob = file.slice(start, end);
  94. // 上传每个分片
  95. cos.multipartUpload({
  96. Task: lastTaskId,
  97. Bucket: Bucket,
  98. Region: Region,
  99. Key: Key,
  100. UploadId: UploadId,
  101. PartNumber: PartNumber,
  102. Body: blob,
  103. }, function (err, data) {
  104. if (!cos._isRunningTask(TaskId)) return;
  105. if (err) return nextPart(err);
  106. if (!data.headers.etag) return nextPart('浏览器获取不到 ETag Header,需要存储桶配置 CORS ExposeHeaders 允许当前域名跨域读取 ETag 字段。');
  107. partItem.ETag = data.headers.etag || '';
  108. console.log('分片上传完成:', partItem.PartNumber, partItem.ETag);
  109. nextPart();
  110. });
  111. }, function (err) {
  112. if (!cos._isRunningTask(TaskId)) return;
  113. if (err) {
  114. taskStateMap[TaskId] = 'error';
  115. return console.error('上传分片出错:', err);
  116. }
  117. // 完成分片上传
  118. cos.multipartComplete({
  119. Bucket: Bucket,
  120. Region: Region,
  121. Key: Key,
  122. UploadId: UploadId,
  123. Parts: Parts,
  124. }, function (err, data) {
  125. if (err) {
  126. taskStateMap[TaskId] = 'error';
  127. return console.error('文件完成出错:', err);
  128. }
  129. console.log('文件上传成功:', data.Location);
  130. taskStateMap[TaskId] = 'success';
  131. callback(err, data);
  132. });
  133. });
  134. });
  135. };
  136. // 监听表单提交
  137. document.getElementById('submitBtn').onclick = function (e) {
  138. var file = document.getElementById('fileSelector').files[0];
  139. if (!file) {
  140. document.getElementById('msg').innerText = '未选择上传文件';
  141. return;
  142. }
  143. file && uploadFile(file, function (err, data) {
  144. console.log(err || data);
  145. document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
  146. });
  147. };
  148. // 监听取消上传
  149. document.getElementById('cancelBtn').onclick = function (e) {
  150. // 标记任务取消,让 SDK 外部的上传流程停止
  151. taskStateMap[lastTaskId] = 'canceled';
  152. // 取消 SDK 正在执行的 xhr 上传请求,触发 xhr.abort()
  153. cos.cancelTask(lastTaskId);
  154. console.log('上传任务已取消:' + lastTaskId);
  155. };
  156. })();
  157. </script>
  158. </body>
  159. </html>