当前位置:   article > 正文

在vue3项目中实现文件分片上传vue-simple-uploader_vue3分片上传插件

vue3分片上传插件
vue-simple-uploader 是一个基于 Vue.js 的简单文件上传插件,可支持文件分片上传、断点续传以及秒传,有以下几个特点:

1、简单易用:只需在 Vue 组件中使用该插件,并设置相应的配置选项即可实现文件上传功能。

2、多文件上传:插件支持同时上传多个文件,可以选择并一次性上传多个文件。

3、进度条显示:该插件提供了进度条显示上传进度,让用户清楚地知道文件上传的状态和进度。

4、上传前验证:可以通过设置验证规则,对上传的文件进行前端验证。例如,限制文件类型、大小等,以确保只上传符合要求的文件。

5、自定义选项:插件提供了一些可自定义的选项,如上传地址、请求头部、参数等,以满足不同的业务需求。

6、事件回调:提供了丰富的事件回调函数,允许在不同的上传阶段执行自定义逻辑,如上传前、上传成功、上传失败等。

总之,Vue Simple Uploader 简化了文件上传的过程,并提供了一些有用的功能和灵活的配置选项,易于集成和使用。

最终成功效果

1、首先需要下载依赖,vue-simple-uploader、spark-md5

npm i --save vue-simple-uploader@next spark-md5

下载好的版本号是1.0.1,这个版本是支持vue3的,不加@next 默认是0.7.6,是只支持vue2的,vue3中使用会报错。

2、main.js 注册
  1. import { createApp } from 'vue';
  2. import App from './App.vue';
  3. // 文件分片上传
  4. import uploader from 'vue-simple-uploader';
  5. import 'vue-simple-uploader/dist/style.css';
  6. createApp(App).use(uploader).mount('#app');
3、编写文件类型 accept.config.js
  1. export const ACCEPT_CONFIG = {
  2. image: ['.png', '.jpg', '.jpeg', '.gif', '.bmp'],
  3. video: ['.mp4', '.rmvb', '.mkv', '.wmv', '.flv'],
  4. document: [
  5. '.doc',
  6. '.docx',
  7. '.xls',
  8. '.xlsx',
  9. '.ppt',
  10. '.pptx',
  11. '.pdf',
  12. '.txt',
  13. '.tif',
  14. '.tiff',
  15. '.rar',
  16. '.zip',
  17. ],
  18. getAll() {
  19. return [...this.image, ...this.video, ...this.document];
  20. },
  21. };
4、编写upload.vue
  1. <template>
  2. <div>
  3. <!-- 上传器 -->
  4. <uploader
  5. ref="uploaderRef"
  6. :options="options"
  7. :autoStart="false"
  8. :file-status-text="fileStatusText"
  9. class="uploader-ui"
  10. @file-added="onFileAdded"
  11. @file-success="onFileSuccess"
  12. @file-progress="onFileProgress"
  13. @file-error="onFileError"
  14. >
  15. <uploader-unsupport></uploader-unsupport>
  16. <uploader-drop>
  17. <div>
  18. <uploader-btn id="global-uploader-btn" ref="uploadBtn" :attrs="attrs"
  19. >选择文件<i class="el-icon-upload el-icon--right"></i
  20. ></uploader-btn>
  21. </div>
  22. </uploader-drop>
  23. <uploader-list></uploader-list>
  24. </uploader>
  25. </div>
  26. </template>
  27. <script setup>
  28. import { ACCEPT_CONFIG } from '@/config/accept.config';
  29. import { reactive, ref } from 'vue';
  30. import SparkMD5 from 'spark-md5';
  31. import { mergeFile } from '@/api/demo/fileUpload/index';
  32. import { ElMessage } from 'element-plus';
  33. const options = reactive({
  34. //目标上传 URL,默认POST, import.meta.env.VITE_API_URL = api
  35. // target ==》http://localhost:6666/api/uploader/chunk
  36. target: import.meta.env.VITE_API_URL + '/uploader/chunk',
  37. query: {},
  38. headers: {
  39. // 需要携带token信息,当然看各项目情况具体定义
  40. token: "your_token",
  41. },
  42. //分块大小(单位:字节)
  43. chunkSize: '2048000',
  44. //上传文件时文件内容的参数名,对应chunk里的Multipart对象名,默认对象名为file
  45. fileParameterName: 'upfile',
  46. //失败后最多自动重试上传次数
  47. maxChunkRetries: 3,
  48. //是否开启服务器分片校验,对应GET类型同名的target URL
  49. testChunks: true,
  50. // 服务器分片校验函数
  51. checkChunkUploadedByResponse: function (chunk, response_msg) {
  52. let objMessage = JSON.parse(response_msg);
  53. console.log(response_msg, 'response_msg');
  54. if (objMessage.skipUpload) {
  55. return true;
  56. }
  57. return (objMessage.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0;
  58. },
  59. });
  60. const attrs = reactive({
  61. accept: ACCEPT_CONFIG.getAll(),
  62. });
  63. const fileStatusText = reactive({
  64. success: '上传成功',
  65. error: '上传失败',
  66. uploading: '上传中',
  67. paused: '暂停',
  68. waiting: '等待上传',
  69. });
  70. onMounted(() => {
  71. console.log(uploaderRef.value, 'uploaderRef.value');
  72. });
  73. function onFileAdded(file) {
  74. computeMD5(file);
  75. }
  76. function onFileSuccess(rootFile, file, response, chunk) {
  77. //refProjectId为预留字段,可关联附件所属目标,例如所属档案,所属工程等
  78. file.refProjectId = '123456789';
  79. mergeFile(file)
  80. .then((responseData) => {
  81. if (responseData.data.code === 415) {
  82. console.log('合并操作未成功,结果码:' + responseData.data.code);
  83. }
  84. ElMessage.success(responseData.data);
  85. })
  86. .catch(function (error) {
  87. console.log('合并后捕获的未知异常:' + error);
  88. });
  89. }
  90. function onFileError(rootFile, file, response, chunk) {
  91. console.log('上传完成后异常信息:' + response);
  92. }
  93. /**
  94. * 计算md5,实现断点续传及秒传
  95. * @param file
  96. */
  97. function computeMD5(file) {
  98. file.pause();
  99. //单个文件的大小限制2G
  100. let fileSizeLimit = 2 * 1024 * 1024 * 1024;
  101. console.log('文件大小:' + file.size);
  102. console.log('限制大小:' + fileSizeLimit);
  103. if (file.size > fileSizeLimit) {
  104. file.cancel();
  105. }
  106. let fileReader = new FileReader();
  107. let time = new Date().getTime();
  108. let blobSlice =
  109. File.prototype.slice ||
  110. File.prototype.mozSlice ||
  111. File.prototype.webkitSlice;
  112. let currentChunk = 0;
  113. const chunkSize = 10 * 1024 * 1000;
  114. let chunks = Math.ceil(file.size / chunkSize);
  115. let spark = new SparkMD5.ArrayBuffer();
  116. //由于计算整个文件的Md5太慢,因此采用只计算第1块文件的md5的方式
  117. let chunkNumberMD5 = 1;
  118. loadNext();
  119. fileReader.onload = (e) => {
  120. spark.append(e.target.result);
  121. if (currentChunk < chunkNumberMD5) {
  122. loadNext();
  123. } else {
  124. let md5 = spark.end();
  125. file.uniqueIdentifier = md5;
  126. file.resume();
  127. console.log(
  128. `MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
  129. file.size
  130. } 用时:${new Date().getTime() - time} ms`
  131. );
  132. }
  133. };
  134. fileReader.onerror = function () {
  135. error(`文件${file.name}读取出错,请检查该文件`);
  136. file.cancel();
  137. };
  138. function loadNext() {
  139. let start = currentChunk * chunkSize;
  140. let end = start + chunkSize >= file.size ? file.size : start + chunkSize;
  141. fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
  142. currentChunk++;
  143. console.log('计算第' + currentChunk + '块');
  144. }
  145. }
  146. const uploaderRef = ref();
  147. function close() {
  148. uploaderRef.value.cancel();
  149. }
  150. function error(msg) {
  151. console.log(msg, 'msg');
  152. }
  153. </script>
  154. <style scoped>
  155. .uploader-ui {
  156. padding: 15px;
  157. margin: 40px auto 0;
  158. font-size: 12px;
  159. font-family: Microsoft YaHei;
  160. box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
  161. }
  162. .uploader-ui .uploader-btn {
  163. margin-right: 4px;
  164. font-size: 12px;
  165. border-radius: 3px;
  166. color: #fff;
  167. background-color: #409eff;
  168. border-color: #409eff;
  169. display: inline-block;
  170. line-height: 1;
  171. white-space: nowrap;
  172. }
  173. .uploader-ui .uploader-list {
  174. max-height: 440px;
  175. overflow: auto;
  176. overflow-x: hidden;
  177. overflow-y: auto;
  178. }
  179. </style>
5、这是最后合并需要的接口 mergeFile,这个看各项目是怎么封装request,下面只是个例子
  1. import request from '@/utils/request';
  2. export const mergeFile = (data) => {
  3. return request({
  4. url: '/uploader/mergeFile',
  5. method: 'POST',
  6. data,
  7. });
  8. };
6、使用
  1. <template>
  2. <div>
  3. <UploadVue />
  4. </div>
  5. </template>
  6. <script setup>
  7. import UploadVue from './Upload.vue';
  8. </script>

上传文件后端一共需要写三个接口,其中给到前端只需要两个即可,如下:

1、/uploader/chunk:upload.vue文件的options对象下的target,这里需要注意接口携带token的问题,也有些项目是cookie,还有就是跨域的问题,也就是为什么要用import.meta.env.VITE_API_URL进行拼接的原因,本地联调的话因为VITE_APP_URL环境变量设置的是 /api ,所以是http://localhost:6666/api/uploader/chunk

2、/uploader/mergeFile:这个是将上传文件成功后的各分片最后合并的接口

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号