直接附上代码具体内容代码中注释都有,这个只是上传的模板,具体请求和要传递的参数需要自己和后端去对接
使用spark-md5 根据文件的 ArrayBuffer 格式将文件的内容生成一个hash值,来表示文件的信息
<template>
<div class="box">
<div class="upload-box">
<!-- 通过accept 来限制文件类型,需要用逗号将文件后缀名隔开 -->
<!-- <input ref="inp" type="file" style="display: none" @change="inpChange" accept=".png,.jpg"/> -->
<input
ref="inp"
type="file"
style="display: none"
@change="inpChange"
multiple
/>
<div class="operate">
<div>
<el-button type="primary" :loading="isUploading" @click="clickHandler"
>选择文件</el-button
>
<el-button type="success" :loading="isUploading" @click="uploadHandler"
>上传文件</el-button
>
</div>
<el-button
type="text"
@click="emptyHandler"
v-if="this.files.length != 0"
:disabled="isUploading"
>清空</el-button
>
</div>
<div class="list">
<div
class="upload-show"
v-for="(item, index) in files"
:key="item.file.name"
>
<div class="icon-progress-box">
<i
class="el-icon-document transform icon"
style="font-size: 20px"
></i>
<div class="upload_progress">
<!--进度条 -->
<div
class="upload_progress-item"
:style="{ width: item.progress + '%' }"
></div>
<span class="chart">{{ item.file.name }}</span>
</div>
</div>
<el-button
type="text"
@click="delHandler(index)"
style="color: red"
class="transform"
:disabled="isUploading||item.state"
>移除</el-button
>
</div>
</div>
</div>
</div>
</template>
<script>
import SparkMD5 from "spark-md5";
export default {
name: "APP",
data() {
return {
files: [], // 存储选中的文件及其上传进度
isUploading: false, // 上传状态
completeList:[],//已经上传完成的数据
};
},
methods: {
clickHandler() {
// 触发文件输入框的点击事件
this.$refs.inp.click();
},
// 将文件转换为 ArrayBuffer 格式,并生成 MD5 哈希值
changeBuffer(file) {
return new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(file); // 读取文件为 ArrayBuffer
fileReader.onload = (e) => {
let buffer = e.target.result; // 获取文件的 ArrayBuffer
const spark = new SparkMD5.ArrayBuffer();
spark.append(buffer); // 计算 MD5 哈希值
let hash = spark.end();
let suffix = /\.([a-zA-Z0-9]+)$/.exec(file.name)[1]; // 获取文件后缀名
resolve({
hash,
suffix,
filename: `${hash}.${suffix}`, // 生成新的文件名
});
};
});
},
// 处理文件选择事件
async inpChange(e) {
console.log('选择文件事件对象',e.target.files)
// 将选中的文件转为数组,并为每个文件添加一个进度属性
const arr = Array.from(e.target.files).map((file) => ({
file,
progress: 0,
}));
console.log(77777,arr)
const tempArr = [...this.files, ...arr];
this.files = Array.from(new Set(tempArr.map((f) => f.file.name))).map(
(name) => {
return tempArr.find((f) => f.file.name === name);
}
);
console.log(this.files); // 输出选中的文件信息
},
// 删除选中的文件
delHandler(type) {
this.files = this.files.filter((item, index) => {
return index != type;
});
this.$refs.inp.value = ''; // 重置文件输入框的值,input的change 事件
},
// 清空文件列表
emptyHandler() {
this.files = this.files.filter(item=>item.state);
this.$refs.inp.value = ''; // 重置文件输入框的值
},
// 上传单个文件的分片
async uploadFile(fileObj) {
console.log(6666666,fileObj)
if (fileObj.state) {
alert("该文件已上传");
return;
}
let { file } = fileObj;
let { hash, suffix } = await this.changeBuffer(file); // 获取文件的哈希值和后缀名
//
console.log(111, hash);
console.log(111, suffix);
let chunks = []; // 文件分片数组
let max = 1024 * 1024; // 分片大小,1MB
let count = Math.ceil(file.size / max); // 分片数量
// 创建文件分片
for (let i = 0; i < count; i++) {
chunks.push({
file: file.slice(i * max, (i + 1) * max),
filename: `${hash}_${i}.${suffix}`, // 生成分片文件名
});
}
console.log(chunks);
// 上传分片的递归函数
const uploadChunk = async (index) => {
if (index >= chunks.length) {
// 此处代表分片上传成功进行merag 合并
console.log(`${file.name} 上传完成`); // 输出上传完成信息
return;
}
let item = chunks[index]; //通过索引获取到每一片分片的blob
let fm = new FormData();
fm.append("file", item.file); // 将分片文件添加到 FormData
fm.append("filename", item.filename); // 添加分片文件名到 FormData
// 模拟上传,实际应用中应替换为 HTTP 请求
await new Promise((resolve) => setTimeout(resolve, 100)); // 模拟网络延迟
// 更新进度,去到最小值,当全部上传后进度条,为100
fileObj.progress = Math.min(((index + 1) / count) * 100, 100);
// 递归上传下一个分片
await uploadChunk(index + 1);
};
// 开始上传第一个分片
await uploadChunk(0);
},
// 触发文件上传
async uploadHandler() {
if (this.files.length === 0) {
alert("请先选择文件"); // 如果没有选择文件,提示用户
return;
}
this.isUploading = true; // 开始上传时设置loading状态
const uploadPromises = this.files.map((fileObj) =>
this.uploadFile(fileObj)
);
await Promise.all(uploadPromises).then(() => {
this.isUploading = false; // 上传完成后移除loading状态
this.files.forEach((item) => {
item.state = true;
});
this.completeList = [...this.files]
}); // 等待所有文件上传完成
console.log('completeList',this.completeList)
},
},
};
</script>
<style scoped>
.box {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
}
.upload-box {
width: 550px;
height: 260px;
border: 1px dashed #000;
padding: 10px;
}
.operate {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
}
.list {
width: 100%;
height: calc(100% - 60px);
overflow: auto;
margin-top: 10px;
}
.transform {
transform: translateY(5px);
}
.upload-show {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 10px;
border-bottom: 1px solid #bbb;
}
.icon-progress-box {
display: flex;
align-items: center;
}
.icon {
font-size: 20px;
margin: 0 10px 0 15px ;
}
.upload_progress {
width: 430px;
height: 25px;
background-color: #e2e2e2;
position: relative;
margin-top: 10px;
border-radius: 10px;
}
.upload_progress-item {
height: 100%;
border-radius: 10px;
background-color: #8ee700;
position: absolute;
bottom: 0%;
top: 0;
right: 0;
left: 0;
}
.chart {
height: 100%;
position: absolute;
bottom: 0%;
top: 0;
right: 0;
left: 0;
z-index: 9;
display: flex;
align-items: center;
padding-left: 20px;
font-size: 14px;
}
</style>