Vue3+Ts的大文件分片上传,秒传,断点续传

本文介绍了如何使用Vue3和TypeScript开发一个支持大文件分片上传、秒传和断点续传的组件,通过分片上传技术提高稳定性、进度展示和上传效率,以提升用户在大文件上传过程中的体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue3+Ts的大文件分片上传,秒传,断点续传
1.为什么要使用分片上传
分片上传(Chunked Upload)是一种将大文件分割成多个较小的部分进行上传的技术。这种技术有几个优点:

稳定性: 上传大文件时,如果整个文件一次性上传,可能会因为网络不稳定或其他原因导致上传中断。使用分片上传可以降低上传失败的风险,因为只需要重新上传失败的部分,而不是整个文件。

进度显示: 对于大文件的一次性上传,用户可能会感觉上传过程很慢,因为他们无法看到上传的进度。而使用分片上传,可以在每个分片上传完成后更新上传进度,让用户更清楚地了解上传的进度。

并行处理: 分片上传可以让客户端同时上传多个分片,从而提高上传效率。这对于大文件上传来说尤为重要,因为这样可以充分利用网络带宽,加快上传速度。

断点续传: 如果上传过程中断,用户可以从已经上传完成的分片继续上传,而不需要重新上传整个文件。这样可以节省时间和带宽,并提升用户体验。

总的来说,分片上传可以提高大文件上传的稳定性、效率和用户体验,因此在需要上传大文件的应用场景中,使用分片上传是一种常见的做法。

下面我们来开始开发此项功能:实现大文件分片上传,秒传,断点续传,进度管控

1.技术栈
所用技术为Vue3+TSUI框架使用arco-design,接口请求使用fetch
2.构建页面:构建上传组件,以及进度条控制
<template>
  <div>
    <a-upload :auto-upload="false" ref="uploadRef" @change="fileChange" multiple draggable>
    </a-upload>
        <a-progress
        :percent="percentage"
        :style="{ width: '50%' }"
        :color="{
          '0%': 'rgb(var(--primary-6))',
          '100%': 'rgb(var(--success-6))',
        }"
      />
    <a-button type="primary" @click="submit"> start upload</a-button>
  </div>
</template>
3.获取文件进行分片,并获取文件唯一的hash值
import { ref } from "vue";
import SparkMD5 from "spark-md5";
import { Message } from "@arco-design/web-vue";

const uploadRef = ref()
const percentage = ref()
let chunkSize = 1024 * 512
// 存放所有分片内容
let chunkRes = ref()
let fileName = ref('')
let fileHash = ref('')

const fileChange = async (value: any) => {
  // 获取文件
  const files = value[0].file
  if (!files) {
    return
  }
  fileName.value = files.name
  chunkRes.value = createFileChunk(files) //分片
  const res = await getUniqueHash(chunkRes.value) //生成hash值
  fileHash.value = res as string
}

// 执行文件分片操作
const createFileChunk = (file: File) => {
  const chunkList = [];
  let cur = 0;
  while (cur < file.size) {
    chunkList.push(file.slice(cur, cur + chunkSize));
    cur += chunkSize;
  }
  return chunkList;
};

// 使用hash识别文件,实现秒传的功能
const getUniqueHash = (fileChunks: any) => {
  return new Promise(resolve => {
    const spark = new SparkMD5.ArrayBuffer()
    const fileReader = new FileReader()
    const target: Blob[] = []
    fileChunks.forEach((item: Blob, index: number) => {
      if (index === 0 || index === fileChunks.length - 1) {
        target.push(item)
      } else {
        // 当前切片的前面2字节
        target.push(item.slice(0, 2))
        // 中间的两个字节
        target.push(item.slice(chunkSize / 2, chunkSize / 2 + 2))
        // 后面的两个字节
        target.push(item.slice(chunkSize - 2, chunkSize))
      }
    })
    fileReader.readAsArrayBuffer(new Blob(target)) //将target中的Blob对象转化为ArrayBuffer
    fileReader.onload = (e: any) => { //onload方法是异步的
      spark.append(e.target?.result) //SparkMD5 库的 append 方法将读取的 ArrayBuffer 添加到 MD5 计算中
      resolve(spark.end())
    }
  })
}

4.上传分片,并识别当前的分片是否已经上传实现断点续传

// 上传分片 chunkRes是所有分片 data.existsChunks是已经存在的分片  
const uploadChunk = async (chunkRes: any, existsChunks:string[]) => {
  // 处理数据成我们需要的格式
  const data = chunkRes.map((item: Blob, index: number) => {
    return {
      fileHash: fileHash.value,
      chunkHash: `${fileHash.value}-${index}`,
      chunk: item,
      size: item.size,
    }
  })
  // 添加为formData对象
  const formDatas = data
  .filter((item:any) => !existsChunks.includes(item.chunkHash))
  .map((item: any) => {
    const formData = new FormData()
    formData.append('chunk', item.chunk)
    formData.append('chunkHash', item.chunkHash)
    formData.append('fileName', fileName.value)
    formData.append('fileHash', fileHash.value)
    return formData
  })
  // 控制它的最大请求书
  let max = 1
  let index = 0 //文件是否上传完毕控制
  const taskPool: any = [] //文件控制池
  while (index < formDatas.length) {
    const task = fetch('http://localhost:3000/upload', {
      method: 'POST',
      body: formDatas[index]
    })
    // 当任务执行完成之后,删除taskPool内已完成的数据
    task.then(() => {
      taskPool.splice(taskPool.findIndex((item: any) => item === task))
    })
    taskPool.push(task)
    // 当数组中的数据为6的时候,循环稍等,等其中一个完成再继续
    if (taskPool.length === max) {
      await Promise.race(taskPool)
    }
    console.log(index,'indx')
    console.log(formDatas.length,'formDatas.length')
    console.log(percentage.value)
    index++
    percentage.value = (index / formDatas.length).toFixed(2)
  }
  // 保险起见,将其中的任务再执行一遍
  await Promise.all(taskPool)
  // 所有的文件都上传完毕了,实现合并请求
  mergeFile()
}

5.提交请求,实现秒传以及文件合并


const mergeFile = async () => {
  const res = await fetch('http://localhost:3000/merge', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      fileName: fileName.value,
      fileHash: fileHash.value,
      size: chunkSize
    })
  })
  if (res.status === 200) {
    percentage.value = 1
    Message.success({
      content: '文件上传成功'
    })
  }
}

// 秒传
const secondPass = async () => {
  const res = await fetch('http://127.0.0.1:3000/verify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      fileName: fileName.value,
      fileHash: fileHash.value
    })
  })
  const data = await res.json()
  return data
}


const submit = async () => {
  const { data } = await secondPass()
  if (!data.shouldUpload) {
     Message.success({
      content: '文件秒传成功'
    })
    return
  }
  uploadChunk(chunkRes.value,data.existsChunks)
}

### 文件上传方案概述 为了实现基于 `vue-simple-uploader` 的文件上传功能并结合 Spring Boot 后端处理,整个流程可以分为前端配置和后端接收两部分。下面详细介绍具体实施方法。 #### 前端设置 (Vue.js) 对于 Vue 项目,在构建过程中需先通过 npm 安装依赖包: ```bash npm install vue-simple-uploader ``` 随后在项目的入口文件中引入该插件,并将其注册为全局组件以便在整个应用范围内使用[^2]。如果是在 Vue 2.x 版本下操作,则可以在 `main.js` 中完成上述工作;而对于 Vue 3.x 则应在 `main.ts` 或相应的 JavaScript 配置文件里执行相同的操作[^3]。 ```javascript // 对应 Vue 2.x 的 main.js 示例 import Vue from &#39;vue&#39;; import VueSimpleUploader from &#39;vue-simple-uploader&#39;; Vue.use(VueSimpleUploader); ``` ```typescript // 对应 Vue 3.x 的 main.ts 示例 import { createApp } from &#39;vue&#39; import uploader from &#39;vue-simple-uploader&#39; const app = createApp(App) app.use(uploader) ``` 接着,在页面模板内定义用于触发文件选择对话框的按钮以及显示进度条等交互元素的位置。这里给出一段简单的 HTML 结构作为例子: ```html <template> <div id="upload-container"> <!-- 添加样式类名可根据实际需求调整 --> <uploader :options="options" class="uploader-example"></uploader> </div> </template> <script> export default { data() { return { options: { target: &#39;/api/upload&#39;, // 设置请求地址为目标服务器接口路径 testChunks: true, // 开启测试分片选项以支持断点续传特性 simultaneousUploads: 4, chunkSize: 1 * 1024 * 1024 // 单位字节,默认每一片大小设为1MB } }; }, }; </script> ``` 以上代码片段展示了如何利用 `vue-simple-uploader` 组件来初始化上传器实例及其参数设定。其中特别指定了目标 URL (`target`) 和启用分片机制(`testChunks`)两个重要属性,前者决定了提交数据的目标位置而后者则是确保能够正确处理大型文件的关键所在。 #### 后端服务搭建 (Spring Boot) 针对 Spring Boot 方面的工作主要包括创建 RESTful API 来接受来自客户端的数据流,并妥善保存至指定存储介质上。考虑到安全性因素,建议对接收到的内容做必要的校验与过滤后再存入数据库或其他持久化层中。 首先需要添加 Maven 依赖项到 pom.xml 文件之中,确保包含了 Web 支持及相关工具库: ```xml <!-- pom.xml --> <dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 如果还需要连接MySQL则继续加入如下依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> ... </dependencies> ``` 之后编写控制器逻辑响应 HTTP 请求并将接收到的信息写入磁盘或数据库表单记录下来。此处提供了一个简化版的服务端点供参考: ```java @RestController @RequestMapping("/api") public class FileController { @PostMapping(value="/upload", consumes={"multipart/form-data"}) public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file){ try{ byte[] bytes = file.getBytes(); Path path = Paths.get(UPLOAD_DIR + file.getOriginalFilename()); Files.write(path, bytes); return new ResponseEntity<>("
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值