大文件上传(分片上传和断点续传)

1.el-upload标签上传
<template>
  <div>
    <!-- el-upload 组件,用于上传文件 -->
    <el-upload
      ref="upload"
      :action="uploadUrl"
      :before-upload="beforeUpload"
      :on-change="handleChange"
      :on-success="handleSuccess"
      :on-error="handleError"
      :file-list="fileList"
      :auto-upload="false"
    >
      <!-- 文件选择按钮 -->
      <el-button slot="trigger" size="small" type="primary">点击上传</el-button>
      <!-- 上传按钮 -->
      <el-button slot="upload" size="small" type="success" @click="handleUpload">开始上传</el-button>
    </el-upload>

    <!-- 上传进度条 -->
    <el-progress :percentage="progress"></el-progress>
  </div>
</template>
data() {
    return {
      uploadUrl: 'https://your-server-endpoint/upload',  // 后端上传接口地址
      fileList: [],
      progress: 0,  // 上传进度
      chunkSize: 5 * 1024 * 1024,  // 每个分片大小 5MB
    };
  },
计算文件的分片,在文件上传之前
async beforeUpload(file) {
    const totalChunks = Math.ceil(file.size/ this.chunkSize)//计算总的分片数
    const chunkList = [] //存储所有的分片
    // 将文件切割成多个小块
    for(let i = 0;i < totalChunks;i++){
        const start = i * this.chunkSize // 当前分片的起始位置
        const end = Math.min(start + this.chunkSize,file.size) // 当前分片的结束位置
        chunkList.push(file.slice(start,end)) //将文件从 start 到 end 的部分(字节数据)切割出来,并返回一个新的 Blob 对象
    }
    //将分片添加到fileList中,准备上传
    this.fileList = chunkList.map((chunk,index)=>{
        return {
            raw:chunk, // 当前分片
            name:file.fileName, // 文件名
            chunkIndex:index, // 当前分片索引
            totalChunks:totalChunks,// 总分片数
            fileHash:await this.calculateFileHash(file),//文件的唯一标识符  
        }
    })
    return false
}
加密
// 计算文件的哈希值(唯一标识) npm install spark-md5 --save
    calculateFileHash(file) {
      const spark = new SparkMD5.ArrayBuffer();  // 使用 SparkMD5 来计算文件的哈希
      const reader = new FileReader();
      return new Promise((resolve, reject) => {
        reader.onload = function(e) {
          spark.append(e.target.result);
          resolve(spark.end()); // 返回计算出的哈希值
        };
        reader.onerror = function(error) {
          reject(error);
        };
        reader.readAsArrayBuffer(file);  // 读取文件内容
      });
    },
点击开始上传
handleUpload(){
    this.fileList.forEach((fileObj)=>{
        this.uploadChunk(fileObj) //上传每个分片
    })
}
    uploadChunk(){
        // 检查当前分片是否已经上传过
        this.getUploadedChunks(fileObj.name).then(uploadedChunks => {
        if (uploadedChunks.includes(fileObj.chunkIndex)) {
           console.log(`分片 ${fileObj.chunkIndex} 已经上传,跳过`);
           return;  // 如果当前分片已经上传过,则跳过上传
        }
        // 上传未上传的分片
        const formData = new FormData();  // 创建 FormData 实例,准备上传数据
        formData.append('file', fileObj.raw);  // 当前分片
        formData.append('chunkIndex', fileObj.chunkIndex);  // 当前分片的索引
        formData.append('totalChunks', fileObj.totalChunks);  // 总分片数
        formData.append('fileName', fileObj.name);  // 文件名
        formData.append('fileHash', fileObj.fileHash);  // 文件的唯一标识(哈希值)

        // 使用 fetch 发起上传请求
        this.uploadFile(formData, fileObj)
          .then(response => {
            this.handleSuccess(fileObj, response);  // 上传成功后调用
          })
          .catch(error => {
            this.handleError(fileObj, error);  // 上传失败后调用
          });
      });
    }
获取已上传的分片索引

    getUploadedChunks(fileName) {
      return fetch(`https://your-server-endpoint/getUploadedChunks?fileName=${fileName}`)
        .then(response => response.json())
        .then(data => data.uploadedChunks); // 返回已上传分片的索引数组
    },
上传接口调用
    uploadFile(formData, fileObj) {
      return new Promise((resolve, reject) => {
        fetch(this.uploadUrl, {
          method: 'POST',
          headers: this.headers,
          body: formData,
        })
          .then(response => response.json())  // 解析 JSON 响应
          .then(data => {
            if (data.status === 'success') {
              this.updateUploadedChunks(fileObj.name, fileObj.chunkIndex);  // 更新已上传的分片
              resolve(data);  // 上传成功时,返回成功数据
            } else {
              reject(new Error('上传失败'));  // 上传失败时,返回错误
            }
          })
          .catch(err => {
            reject(err);  // 请求失败时,返回错误
          });
      });
    },
    // 更新已上传的分片索引
    updateUploadedChunks(fileName, chunkIndex) {
      let uploadedChunks = JSON.parse(localStorage.getItem(fileName)) || [];
      if (!uploadedChunks.includes(chunkIndex)) {
        uploadedChunks.push(chunkIndex);
      }
      localStorage.setItem(fileName, JSON.stringify(uploadedChunks)); // 保存到 localStorage
    },
posted @   Happy-P  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示