<template>
<div class="info">
<el-input v-model="currentFileName" clearable class="info-content" @clear="resetFile" />
<input
id="file-upload"
ref="fileUpload"
type="file"
@input="uploadFile"
/>
<el-button size="small" class="file-btn" @click="uploadClick">选择</el-button>
<div class="info-tips">仅支持XLS、CSV格式文件,小于等于500M</div>
</div>
<div v-if="fileObj.state" class="uploadChunksPanel">
<div class="progressWrap">
<el-progress text-inside :stroke-width="14" :format="format" :percentage="uploadProgress" />
</div>
<div class="tipsWrap">
<div class="leftBox">
<span :class="[fileObj.state === 3 ? 'blue' : '']">
<i v-if="fileObj.state === 2" class="el-icon-loading"></i>
<i v-if="fileObj.state === 3" class="el-icon-success"></i>
{{ fileObj.tips }}
</span>
</div>
<div v-if="fileObj.name" class="rightBox">
{{ fileObj.name }}<span class="line">/</span>
{{ fileObj.size }}<span v-if="speedStr" class="line">/</span>{{ speedStr }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, unref } from "vue";
const uploadProgress = ref(0); // 上传进度
const fileObj = ref({
name: '',
size: '',
state: 0,
tips: ''
});
const speedStr = ref('');
import BMF from 'browser-md5-file';
// 计算MD5
const toHandleMd5 = (file) => {
return new Promise((resolve, reject) => {
const bmf = new BMF();
bmf.md5(file, (err, md5) => {
err ? reject(err) : resolve(md5);
},
progress => {
fileObj.value.tips = `玩命计算中 ${parseInt(progress * 100)}%,即将开始上传`;
}
);
});
};
// 格式化字节
function formatBytes(value) {
let bytes = '-';
if (value < 1024 * 1024) {
bytes = parseFloat(value / 1024).toFixed(2) + ' KB';
} else if (value >= 1024 * 1024 && value < 1024 * 1024 * 1024) {
bytes = parseFloat(value / (1024 * 1024)).toFixed(2) + ' MB';
} else if (value >= 1024 * 1024 * 1024 && value < 1024 * 1024 * 1024 * 1024) {
bytes = parseFloat(value / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
} else if (value >= 1024 * 1024 * 1024 * 1024) {
bytes = parseFloat(value / (1024 * 1024 * 1024 * 1024)).toFixed(2) + ' TB';
}
return bytes;
}
const uploadFile = async(e) => {
const file = e.target.files[0];
const { size: fileSize, name: fileName } = file;
const maxFileSize = 500 * 1024 * 1024; // 限制文件上传大小 500MB
if (fileSize <= maxFileSize) {
fileObj.value.state = 1;
fileObj.value.name = fileName;
fileObj.value.size = formatBytes(fileSize);
const fileMd5 = await toHandleMd5(file);
fileObj.value.tips = '文件上传中...';
fileObj.value.state = 2;
const sliceSize = 5 * 1024 * 1024; // 切片大小 5MB
const totalChunks = Math.ceil(fileSize / sliceSize); // 总共切片数量 Math.ceil(x) 向上取整
for (let i = 0; i < totalChunks; i++) {
const start = i * sliceSize;
const chunk = file.slice(start, Math.min(fileSize, start + sliceSize)); // Math.min 返回最小的数
const fileIndex = i + 1;
const formData = new FormData();
formData.append('file', chunk); // 当前切片的文件
formData.append('file_name', fileName); // 文件名
formData.append('file_size', fileSize); // 文件总大小
formData.append('file_index', fileIndex); // 当前上传的切片下标,起始1
formData.append('total_chunks', totalChunks); // 切片总数
formData.append('file_md5', fileMd5); // 文件md5值
// 最后一个切片上传时
if (fileIndex + 1 === totalChunks) fileObj.value.tips = '文件合并转存中...';
const sTime = new Date().getTime();
const result = await toUpload(formData); // 上传事件
console.log(result)
const eTime = new Date().getTime();
const speed = sliceSize / ((eTime - sTime) / 1000); // 单个切片上传速度
if (speed < (1024 * 1024)) {
speedStr.value = parseFloat(speed / 1024).toFixed(2) + ' KB/s';
} else if (speed >= (1024 * 1024) && speed < (1024 * 1024 * 1024)) {
speedStr.value = parseFloat(speed / (1024 * 1024)).toFixed(2) + ' MB/s';
}
// 正常操作
if (result?.status) {
uploadProgress.value = Number(((fileIndex / totalChunks) * 100).toFixed(2));
if (result?.data?.is_complete === 1) { // 是否完成 0未完成 1完成
speedStr.value = '';
fileObj.value.tips = '文件上传完成!';
fileObj.value.state = 3;
currentFilePath.value = result?.data?.file_path; // 上传完成时返回的文件路径 用于另一个接口
currentFileName.value = fileName;
}
} else {
resetParams();
console.log('上传失败,请重新上传!');
break;
}
}
} else {
console.log('文件大小超出500MB');
resetParams();
}
};
// 上传
const toUpload = (fileObj) => {
console.log(fileObj)
return
return new Promise((resolve, reject) => {
// uploadChunk 上传接口
uploadChunk(fileObj).then(res => {
resolve(res);
});
});
};
// 重置
const resetParams = () => {
uploadProgress.value = 0;
fileObj.value = {
name: '',
size: '',
state: 0,
tips: ''
};
speedStr.value = '';
};
const fileUpload = ref(null);
const resetFile = () => {
currentFileName.value = '';
currentFilePath.value = '';
fileUpload.value.value = ''; // 清空input选中文件
resetParams();
};
</script>
网友评论