大文件上传(分片上传和断点续传)
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
},
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现