• vue 大文件断点续传、并发控制、请求重试


    1.template

    1. <a-upload :file-list="objData.fileList" name="file" :beforeUpload="beforeUpload" class="zlcUpload">
    2. <a-button>
    3. <a-icon type="cloud-upload" />
    4. </a-button>
    5. </a-upload>
    6. <a-button size="large" type="primary" :disabled="!objData.fileList.length" @click="upload" class="zlcBtn">
    7. 上传
    8. </a-button>

    2.script

    1. data() {
    2. return {
    3. objData: {
    4. chunkSize: 1 * 1024 * 1024,
    5. fileList: [],
    6. sliceList: [],
    7. md5: null,
    8. fileName: null,
    9. finishSliceList: [],
    10. uploadStart: 0
    11. },
    12. visible: false,
    13. uploadCompleted: false,
    14. }
    15. },
    16. methods: {
    17. beforeUpload(file) {
    18. this.objData.fileList = [file];
    19. return false;
    20. },
    21. }

    3.点击上传,进行切片,以及获取文件名,md5唯一标识

    1. async upload() {
    2. this.visible = true;
    3. let file = this.objData.fileList[0];
    4. this.objData.fileName = file.name;
    5. let fileMd5 = await this.getFileMd5(file);
    6. if (file.size < this.objData.chunkSize) {
    7. } else {
    8. this.objData.sliceList = this.getSliceList(file);
    9. this.hadUpload().then(res => {
    10. if (res.message == '上传成功') {
    11. this.objData.uploadStart = this.objData.sliceList.length
    12. this.uploadCompleted = true
    13. return
    14. }
    15. let finishArr = res.result;
    16. this.objData.uploadStart = !finishArr ? this.objData.sliceList.length : finishArr.length;
    17. if (!finishArr.length ||( finishArr.length < this.objData.sliceList.length)) {
    18. //并发请求
    19. this.concurRequest(this.objData.sliceList, 3).then(res => {
    20. let { md5, fileName } = this.objData;
    21. console.log('并发请求结果', res)
    22. let rejectResp = res.some(item => item.code != 200) //是否有失败的
    23. if(!rejectResp){ //全部成功
    24. mergeFile({ md5, fileName }).then(res => {
    25. this.uploadCompleted = true
    26. })
    27. }else{
    28. //至少一个失败
    29. }
    30. })
    31. }else{
    32. }
    33. // //开始上传
    34. // let uploadList = this.objData.sliceList.map(item => {
    35. // if (finishArr.includes(item.flag.toString())) {
    36. // } else {
    37. // return new Promise((resolve, reject) => {
    38. // const formData = new FormData();
    39. // formData.append(`file`, item.blob);
    40. // formData.append(`index`, item.flag);
    41. // formData.append(`md5`, fileMd5);
    42. // formData.append(`filename`, file.name);
    43. // uploadFile(formData).then(res => {
    44. // this.objData.uploadStart++;
    45. // resolve(res);
    46. // }).catch(err => {
    47. // reject(err);
    48. // })
    49. // })
    50. // }
    51. // })
    52. // Promise.allSettled(uploadList).then(res => {
    53. // if (res.some(item => item.status == "rejected")) {
    54. // //至少一个分片失败
    55. // } else {
    56. // let { md5, fileName } = this.objData;
    57. // mergeFile({ md5, fileName }).then(res => {
    58. // this.uploadCompleted = true
    59. // })
    60. // }
    61. // })
    62. })
    63. }
    64. },
    65. //多线程获取md5唯一标识
    66. getFileMd5(file) {
    67. let that = this;
    68. return new Promise(resolve => {
    69. const worker = new Worker(new URL('@src/webWork/fileMd5.js', import.meta.url))
    70. // 向子线程发消息
    71. worker.postMessage(file)
    72. // 接收子线程发来的消息
    73. worker.onmessage = e => {
    74. that.objData.md5 = e.data
    75. resolve(that.objData.md5);
    76. worker.terminate();
    77. }
    78. })
    79. },
    80. //开始切片
    81. getSliceList(file) {
    82. let result = [];
    83. let index = 0;
    84. for (let nowSize = 0; nowSize < file.size; nowSize += this.objData.chunkSize) {
    85. result.push({
    86. flag: index,
    87. blob: file.slice(nowSize, nowSize + this.objData.chunkSize),
    88. })
    89. index++;
    90. }
    91. return result;
    92. },
    93. //判断之前是否上传过
    94. hadUpload() {
    95. let { fileName, md5 } = this.objData;
    96. return new Promise((resolve, reject) => {
    97. hadUploadFile({ fileName, md5 }).then(res => {
    98. resolve(res);
    99. }).catch(err => {
    100. reject();
    101. })
    102. })
    103. },
    104. //并发控制
    105. concurRequest(urls, maxNum){
    106. let that = this
    107. return new Promise(resolve => {
    108. let index = 0; //指向下一次请求的Url对应的下标
    109. let result = [] //保存请求的结果
    110. let count = 0; //当前完成请求的数量
    111. async function _request(){
    112. let i = index
    113. const url = urls[index]
    114. index++;
    115. try{
    116. // const resp = await fetch(url)
    117. const formData = new FormData();
    118. formData.append(`file`, url.blob);
    119. formData.append(`index`, url.flag);
    120. formData.append(`md5`, that.objData.md5);
    121. formData.append(`filename`, that.objData.fileName);
    122. const resp = await uploadFileCopy(formData, 2)
    123. console.log('请求结果', resp)
    124. result[i] = resp
    125. }catch(err){
    126. result[i] = err
    127. }finally{
    128. that.objData.uploadStart++;
    129. count++;
    130. if(count == urls.length){
    131. resolve(result)
    132. }
    133. if(index < urls.length){
    134. _request()
    135. }
    136. }
    137. }
    138. for(let i = 0; i < Math.min(urls.length, maxNum); i++){
    139. _request()
    140. }
    141. })
    142. }

    @src/webWork/fileMd5.js文件内容

    1. // worker.js ---子线程
    2. import SparkMD5 from "spark-md5"
    3. // 接收主线程发来的消息
    4. onmessage = (file) => {
    5. // 向主线程发送消息
    6. const reader = new FileReader();
    7. reader.readAsArrayBuffer(file.data);
    8. reader.onloadend = function (e) {
    9. const spark = new SparkMD5.ArrayBuffer();
    10. spark.append(e.target.result);
    11. let md5 = spark.end();
    12. postMessage(md5);
    13. };
    14. };

    4.请求重试api

    1. //请求重试
    2. export const uploadFileCopy = (params, maxTimes) => {
    3. let initTimes = 0;
    4. let request = () => {
    5. return postRequest("/file/upload", params, {"Content-Type": "multipart/form-data"}).catch(err => {
    6. if(initTimes < maxTimes){
    7. initTimes++
    8. return request()
    9. }else{
    10. throw new Error('请求次数已达上限!')
    11. }
    12. })
    13. }
    14. return request()
    15. }

  • 相关阅读:
    Cesium 实战 - 3dtile 数据与 I3S(ArcGIS 服务) 数据设置透明
    pico+unity3d运行测试方法
    Spring中Bean的作用域和生命周期
    快速解决 Resource not accessible by integration
    Arthas 常用命令
    QSPI介绍
    一道有意思的 CSS 面试题,FizzBu​​zz ~
    uniapp之消除图片的空白占用空间
    C# —— 对象
    python selenium控制浏览器打开网页 模拟鼠标动作
  • 原文地址:https://blog.csdn.net/m0_56104994/article/details/139476487