upload_file_chunk.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. class FileUploader {
  2. constructor({ url, chunkSize = 1024 * 1024, fileName, retryLimit = 3 }) {
  3. this.url = url; // 文件路径
  4. this.chunkSize = chunkSize; // 每片大小,默认为 1MB
  5. this.fileName = fileName; // 文件名
  6. this.fileSystemManager = my.getFileSystemManager(); // 获取文件系统管理器
  7. this.totalChunks = 0; // 总分片数
  8. }
  9. // 开始上传
  10. async startUpload(callback) {
  11. try {
  12. console.log(this.url)
  13. const arrayBuffer = await this.urlToArrayBuffer(this.url);
  14. console.log(arrayBuffer)
  15. // const fileData = await this.readFileAsArrayBuffer(this.filePath);
  16. const fileType = await this.detectFileType(this.filePath);
  17. this.fileType = fileType.type;
  18. this.fileName = this.fileName + fileType.extension;
  19. console.log('detectFileType: success', fileType,arrayBuffer);
  20. this.totalChunks = Math.ceil(arrayBuffer.byteLength / this.chunkSize);
  21. // 逐片上传
  22. for (let chunkIndex = 0; chunkIndex < this.totalChunks; chunkIndex++) {
  23. const chunk = this.getChunk(arrayBuffer, chunkIndex);
  24. const base64Chunk = my.arrayBufferToBase64(chunk);
  25. // 上传分片,带重试机制
  26. let resObj = await this.uploadChunk(base64Chunk, chunkIndex, this.totalChunks);
  27. console.log(`分片 ${chunkIndex + 1}/${this.totalChunks} 上传成功`);
  28. callback('success', resObj)
  29. }
  30. console.log("文件上传成功!");
  31. } catch (error) {
  32. callback('error', `${JSON.stringify(error)}`)
  33. console.error("文件上传失败:", JSON.stringify(error));
  34. }
  35. }
  36. async urlToArrayBuffer(url) {
  37. return new Promise((resolve, reject) => {
  38. // 使用 my.downloadFile 下载文件
  39. my.downloadFile({
  40. url: url,
  41. success: async (downloadRes) => {
  42. console.log(downloadRes)
  43. // const downPath = downloadRes.tempFilePath; // 下载后的路径,后缀名可能被篡改
  44. // this.filePath = downPath
  45. // // 使用文件系统读取文件内容
  46. // this.fileSystemManager.readFile({
  47. // filePath: downPath,
  48. // success: (res) => {
  49. // resolve(res.data); // res.data 是 ArrayBuffer
  50. // },
  51. // fail: (err) => {
  52. // reject(`读取文件失败: ${err}`);
  53. // }
  54. // });
  55. if (downloadRes.statusCode === 200) {
  56. const downPath = downloadRes.tempFilePath; // 下载后的路径,后缀名可能被篡改
  57. this.filePath = downPath
  58. // 使用文件系统读取文件内容
  59. this.fileSystemManager.readFile({
  60. filePath: downPath,
  61. success: (res) => {
  62. resolve(res.data); // res.data 是 ArrayBuffer
  63. },
  64. fail: (err) => {
  65. reject(`读取文件失败: ${JSON.stringify(err)}`);
  66. }
  67. });
  68. } else {
  69. reject(`下载失败,状态码: ${downloadRes.statusCode}`);
  70. }
  71. },
  72. fail: (err) => {
  73. reject(`下载失败: ${JSON.stringify(err)}`);
  74. }
  75. });
  76. });
  77. }
  78. // 读取文件为 ArrayBuffer
  79. readFileAsArrayBuffer(filePath) {
  80. return new Promise((resolve, reject) => {
  81. this.fileSystemManager.readFile({
  82. filePath: filePath,
  83. success: (res) => resolve(res.data),
  84. fail: (err) => reject(err),
  85. });
  86. });
  87. }
  88. // 检测文件类型
  89. detectFileType(filePath) {
  90. console.log(filePath)
  91. return new Promise((resolve, reject) => {
  92. my.detectFileType({
  93. filePath: filePath,
  94. success: (res) => resolve(res),
  95. fail: (err) => reject(err),
  96. });
  97. });
  98. }
  99. // 获取文件的指定分片
  100. getChunk(fileData, chunkIndex) {
  101. const start = chunkIndex * this.chunkSize;
  102. const end = Math.min(fileData.byteLength, start + this.chunkSize);
  103. return fileData.slice(start, end);
  104. }
  105. // 带重试的分片上传
  106. async uploadChunkWithRetry(base64Chunk, chunkIndex) {
  107. let attempt = 0;
  108. while (attempt < this.retryLimit) {
  109. try {
  110. let resObj = await this.uploadChunk(base64Chunk, chunkIndex, this.totalChunks);
  111. console.log(`分片 ${chunkIndex + 1}/${this.totalChunks} 上传成功`);
  112. return resObj; // 上传成功,退出重试循环
  113. } catch (error) {
  114. attempt++;
  115. console.warn(`分片 ${chunkIndex + 1} 上传失败,重试次数:${attempt}/${this.retryLimit}`);
  116. if (attempt >= this.retryLimit) {
  117. throw new Error(`分片 ${chunkIndex + 1} 上传失败,已达最大重试次数`);
  118. }
  119. }
  120. }
  121. }
  122. // 上传单个分片
  123. uploadChunk(base64Chunk, chunkIndex, totalChunks) {
  124. return new Promise((resolve, reject) => {
  125. try {
  126. let params = {
  127. "action": "setStorage",
  128. "event": "",
  129. "taskId": "117",
  130. "params": {
  131. content: base64Chunk, // 当前分片的 Base64 数据
  132. currentIndex: chunkIndex, // 当前分片的索引
  133. totalChunks: totalChunks, // 总分片数
  134. fileName: this.fileName, // 文件名
  135. }
  136. }
  137. console.log(params)
  138. resolve(params);
  139. // my.call("ampeHHCommunication", params, res => {
  140. // console.log('ampeHHCommunication', res)
  141. // console.log(JSON.stringify(res))
  142. // if (res.success) {
  143. // resolve(params);
  144. // }else{
  145. // reject(JSON.stringify(res))
  146. // }
  147. // });
  148. } catch (error) {
  149. reject(error)
  150. }
  151. });
  152. }
  153. }
  154. // 导出类
  155. module.exports = FileUploader;