Vue + Element Plus 实战:大文件切片上传 + 断点续传

好的!下面是一篇结合 Element Plus 的 Vue 3 大文件切片上传 + 断点续传完整实战教程,UI 用的是 Element Plus,结构清晰,语气亲和,适合你直接复制使用。


整理不易,如果本文对你有帮助,欢迎点个【赞 👍】+【收藏 ⭐】+【关注 🧡】

📦 Vue + Element Plus 实战:大文件切片上传 + 断点续传

今天我们来用 Vue 3 + Element Plus,实战开发一个“支持大文件切片上传、断点续传”的文件上传组件,适合上传视频、压缩包、设计图等大文件场景,带完整前端逻辑和 UI 演示👇


🧩一、功能亮点

✅ 文件切片(Blob 分片)
✅ 并发上传,支持进度条
✅ 秒传校验(文件 hash)
✅ 断点续传(跳过已上传)
✅ 合并通知(服务端拼接)


✨二、最终效果图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
(上传进度 + 支持断点续传)


🛠️三、完整前端实现(Element Plus 版本)

1️⃣ 安装依赖

npm install element-plus spark-md5

2️⃣ 组件模板(UploadChunk.vue)

<template>
  <el-card>
    <el-upload
      :show-file-list="false"
      :before-upload="handleBeforeUpload"
      :http-request="handleUpload"
      drag
      accept="*"
    >
      <i class="el-icon-upload" />
      <div class="el-upload__text">将文件拖到此处,或 <em>点击上传</em></div>
    </el-upload>

    <el-progress
      v-if="uploadProgress > 0"
      :percentage="uploadProgress"
      status="success"
      style="margin-top: 20px"
    />
  </el-card>
</template>

3️⃣ 脚本逻辑(切片、上传、断点续传)

<script setup>
import { ref } from 'vue'
import SparkMD5 from 'spark-md5'
import { ElMessage } from 'element-plus'

const uploadProgress = ref(0)
const CHUNK_SIZE = 2 * 1024 * 1024 // 2MB

let file = null
let fileHash = ''
let fileChunks = []

// 1. 预处理文件
const handleBeforeUpload = async (rawFile) => {
  file = rawFile
  fileChunks = createChunks(file)
  fileHash = await calculateHash(fileChunks)
  return false // 阻止默认上传
}

// 2. 分片
const createChunks = (file) => {
  const chunks = []
  let cur = 0
  while (cur < file.size) {
    chunks.push(file.slice(cur, cur + CHUNK_SIZE))
    cur += CHUNK_SIZE
  }
  return chunks
}

// 3. 计算 hash
const calculateHash = (chunks) => {
  return new Promise((resolve) => {
    const spark = new SparkMD5.ArrayBuffer()
    let count = 0
    const reader = new FileReader()

    const loadNext = () => {
      if (count >= chunks.length) {
        resolve(spark.end())
        return
      }
      reader.readAsArrayBuffer(chunks[count])
    }

    reader.onload = (e) => {
      spark.append(e.target.result)
      count++
      loadNext()
    }

    loadNext()
  })
}

// 4. 获取已上传切片(断点续传)
const getUploadedList = async (hash) => {
  const res = await fetch(`/uploaded-list?hash=${hash}`)
  return res.ok ? await res.json() : []
}

// 5. 上传逻辑
const handleUpload = async () => {
  const uploaded = await getUploadedList(fileHash)

  const uploadChunk = (chunk, index) => {
    const form = new FormData()
    form.append('file', chunk)
    form.append('filename', file.name)
    form.append('chunkIndex', index)
    form.append('hash', fileHash)

    return fetch('/upload-chunk', {
      method: 'POST',
      body: form,
    })
  }

  const requests = fileChunks.map((chunk, index) => {
    if (uploaded.includes(index)) return null
    return uploadChunk(chunk, index)
  }).filter(Boolean)

  let completed = uploaded.length
  const total = fileChunks.length

  for (const req of requests) {
    await req
    completed++
    uploadProgress.value = Math.floor((completed / total) * 100)
  }

  // 通知合并
  await fetch('/merge-chunks', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ filename: file.name, hash: fileHash }),
  })

  ElMessage.success('上传成功 🎉')
  uploadProgress.value = 0
}
</script>

📡四、服务端接口规范(参考 Node)

接口说明:

接口描述
GET /uploaded-list?hash=xxx返回已上传切片 index 列表
POST /upload-chunk接收切片文件,保存到 /tmp/hash/index
POST /merge-chunks合并切片为完整文件,保存在 /uploads/

服务端可用 fs-extramulter 等处理上传,你需要支持以下流程:

  • 创建临时目录保存切片 /tmp/<hash>/index
  • 按顺序读取切片合并 fs.appendFileSync
  • 合并完成后清理切片

🧠五、总结回顾

功能实现点
切片上传Blob.slice() 分片,前端循环上传
秒传前端计算 hash,服务端校验是否上传过
断点续传获取已上传列表,跳过上传
合并通知上传完毕后请求服务端合并接口
UI 展示Element Plus 上传组件 + 进度条反馈
### 如何在 Vue3 中使用 Element Plus 实现文件上传功能 #### 1. 安装依赖 为了在 Vue3 项目中使用 `Element Plus` 和其组件 `el-upload`,需要先安装必要的依赖项。可以通过以下命令完成安装: ```bash npm install element-plus axios ``` 如果尚未初始化项目,则可以按照引用中的说明创建一个新的 Vite + Vue3 + TypeScript 项目[^3]。 --- #### 2. 配置 Element Plus 在项目的入口文件(通常是 `main.ts` 或 `main.js`)中引入并注册 `Element Plus` 及其样式文件: ```typescript import { createApp } from 'vue'; import App from './App.vue'; import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; const app = createApp(App); app.use(ElementPlus); app.mount('#app'); ``` 这一步确保了整个应用都可以正常使用 `Element Plus` 提供的功能和样式[^1]。 --- #### 3. 使用 `el-upload` 进行基本文件上传 以下是基于 `el-upload` 的简单文件上传示例代码: ```html <template> <div class="upload-container"> <!-- el-upload 组件 --> <el-upload action="https://jsonplaceholder.typicode.com/posts/" :on-preview="handlePreview" :on-remove="handleRemove" :before-upload="beforeUpload" multiple :limit="3" :on-exceed="handleExceed" > <el-button type="primary">点击上传</el-button> <template #tip> <div class="el-upload__tip">仅允许上传 jpg/png 文件,且每张大小不超过 500KB。</div> </template> </el-upload> <!-- 图片预览对话框 --> <el-dialog v-model="dialogVisible" title="图片预览"> <img :src="previewUrl" alt="Preview Image" style="width: 100%;" /> </el-dialog> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const dialogVisible = ref(false); // 控制弹窗显示状态 const previewUrl = ref(''); // 存储当前预览的图片 URL // 处理文件移除事件 const handleRemove = (file: any, fileList: any[]) => { console.log(file, fileList); }; // 处理文件预览事件 const handlePreview = (file: any) => { dialogVisible.value = true; previewUrl.value = file.url || file.raw; // 获取文件链接或原始数据 }; // 超过数量限制时触发 const handleExceed = () => { this.$message.warning('最多只能上传 3 个文件!'); }; // 上传前校验文件类型和大小 const beforeUpload = (rawFile: File) => { if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') { ElMessage.error('仅支持 jpeg 和 png 格式的图片!'); return false; } if (rawFile.size / 1024 / 1024 > 0.5) { ElMessage.error('单个文件大小不得超过 500 KB!'); return false; } return true; }; return { dialogVisible, previewUrl, handleRemove, handlePreview, handleExceed, beforeUpload, }; }, }); </script> ``` 上述代码实现了基础的文件上传功能,并提供了文件类型的验证以及最大尺寸的控制。 --- #### 4. 切片分段上传大文件 对于超大文件(如 MP4 视频),推荐采用切片分段的方式上传。这种方式能够有效减少内存占用,并支持断点续传等功能[^2]。 ##### 关键逻辑描述: - 将文件按固定大小分割成多个片段。 - 对每个片段单独发送请求至服务器。 - 在服务端重新组合这些片段为完整的文件。 ##### 示例代码如下: ```javascript function sliceAndUpload(file: File): void { const chunkSize = 1 * 1024 * 1024; // 每次上传 1MB 数据 let start = 0; let end = Math.min(chunkSize, file.size); while (start < file.size) { const formData = new FormData(); const blob = file.slice(start, end); // 创建 Blob 片段 formData.append('chunk', blob); // 添加到表单对象 formData.append('filename', file.name); // 记录原文件名 formData.append('index', String(Math.floor(start / chunkSize))); // 当前片段索引 uploadChunk(formData).then(() => { start = end; end = Math.min(end + chunkSize, file.size); }); } function uploadChunk(data: FormData): Promise<void> { return fetch('/api/upload-chunk', { method: 'POST', body: data, }).then((response) => response.json()); } } ``` 通过调用此函数即可实现对任意大小文件的安全高效传输。 --- #### 5. 推荐项目结构与资源 若希望快速搭建一个带有管理后台样式的框架,可参考开源项目 **element-plus-admin**,该项目已集成 Vite、TypeScript 和 Element Plus 等技术栈[^4]。 访问地址:[https://gitcode.com/gh_mirrors/el/element-plus-admin](https://gitcode.com/gh_mirrors/el/element-plus-admin) --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值