20G
的镜像文件,并进显示当前上传进度vue3.x
+Element Plus
组件+axios
Vue
的元素代码如下,主要借助el-upload
组件:<template>
...
<el-upload
:show-file-list="false"
action
class="mirror-upload"
:http-request="putinMirror"
>
<button>上传环境镜像button>
el-upload>
...
<el-progress
:percentage="progress"
:indeterminate="true"
/>
...
template>
<script setup>
// 引入封装好的接口api,根据提供的接口文档自行封装即可
import {
// 普通get请求api
checkMirrorFileApi,
// 普通post请求api
uploadShardFileApi,
} from "@/assets/api/uploadApi.js"
import { ref } from 'vue'
// 文件输进度条
const progress = ref(0)
...
script>
/* 上传环境镜像 分片上传 */
const putinMirror = async (file) => {
// 校验文件是否符合规范(注意这里的异步方法,因为调用了接口加上await,校验函数若不调用接口可以不写await,否则返回promise对象)
if (await checkMirrorFile(file)) {
// 文件相关信息
let files = file.file
// 从0开始的切片
let shardIndex = 0
// 调用切片方法
uploadFile(files, shardIndex)
}
}
/* 校验上传镜像文件是否符合规范 */
const checkMirrorFile = async (file) => {
// 校验文件格式是否正确,支持.acow2/.iso/.ovf/.zip/.tar
let fileType = file.file.name.split('.')
if (fileType[fileType.length - 1] !== 'acow2' && fileType[fileType.length - 1] !== 'iso' && fileType[fileType.length - 1] !== 'ovf' && fileType[fileType.length - 1] !== 'zip' && fileType[fileType.length - 1] !== 'tar') {
ElMessage.warning('文件格式错误,仅支持.acow2/.iso/.ovf/.zip/.tar')
return false
}
// 校验文件大小是否满足
let fileSize = file.file.size
//文件大小是否超出20G
if (fileSize > 20 * 1024 * 1024 * 1024) {
ElMessage.warning('上传文件大小不超过20G')
return false
}
const res = await checkMirrorFileApi()
if (res.code !== 200) {
ElMessage.warning('暂时无法查看磁盘可用空间,请重试')
return false
}
// 查看磁盘容量大小
if (res.data.diskDevInfos && res.data.diskDevInfos.length > 0) {
let saveSize = 0
res.data.diskDevInfos.forEach(i => {
// 磁盘空间赋值
if (i.devName === '/dev/mapper/centos-root') {
// 返回值为GB,转为字节B
saveSize = i.free * 1024 * 1024 * 1024
}
})
// 上传的文件大小没有超出磁盘可用空间
if (fileSize < saveSize) {
return true
} else {
ElMessage.warning('文件大小超出磁盘可用空间容量')
return false
}
} else {
ElMessage.warning('文件大小超出磁盘可用空间容量')
return false
}
}
/* 文件切片上传 */
const uploadFile = async (file, shardIndex, createTime, savePath, relativePath, timeMillis) => {
// 文件名
let name = file.name
// 文件大小
let size = file.size
// 分片大小
let shardSize = 1024 * 1024 * 5
// 分片总数
let shardTotal = Math.ceil(size / shardSize)
if (shardIndex >= shardTotal) {
isAlive.value = false
progress.value = 100
return
}
// 文件开始结束的位置
let start = shardIndex * shardSize
let end = Math.min(start + shardSize, size)
// 开始切割
let packet = file.slice(start, end)
let formData = new FormData()
formData.append("file", packet)
formData.append("fileName", name)
formData.append("size", size)
formData.append("shardIndex", shardIndex)
formData.append("shardSize", shardSize)
formData.append("shardTotal", shardTotal)
// 下面这些值是后端组装切片时需要的,跟前端关系不大
if (createTime) formData.append("createTime", createTime)
if (savePath) formData.append("savePath", savePath)
if (shardIndex < shardTotal) {
// 进度条保留两位小数展示
progress.value = ((shardIndex / shardTotal) * 100).toFixed(2) * 1
const res = await uploadShardFileApi(formData)
if (res.code !== 200) {
ElMessage.error(res.msg)
progress.value = 0
return
}
if (res.msg == '上传成功' && res.data.fileName && res.data.filePath) {
// 这里为所有切片上传成功后进行的操作
...
}
shardIndex++
uploadFile(file, shardIndex, res.data.createTime, res.data.savePath, res.data.relativePath, res.data.timeMillis)
}
}
Nginx
,我这里的每一个切片文件的大小为5MB
,但是上传第一片的时候会报413的状态码,因为Nginx
默认上传文件的大小是1M
,所以叫运维或者后端同学改一下配置参数,保证文件传输时不会受到服务器的限制20G
文件的下载也是需要考虑的,我与后端小伙伴尝试过通过range
推流的方式来处理大文件的下载,当下载时除了控制台能看到后一直在推流过来,界面上不会出现下载进度的窗口,而且当文件大小超过2G
时会出现浏览器缓存不足导致推流的中断,这里没有系统研究具体原因Nginx
配置访问到文件存储的位置也可以,前端则通过创建a
标签的方式进行下载,这样可以直接调用到浏览器自带的下载直接显示出来当前文件下载的相关信息:下载进度、传输的速率和文件大小,用户体验更好,代码如下:const downloadMirror = async (item) => {
let t = {
id: item.id,
}
const res = await downloadMirrorApi(t)
if (res.headers["content-disposition"]) {
let temp = res.headers["content-disposition"].split(";")[1].split("filename=")[1]
let fileName = decodeURIComponent(temp)
// 通过创建a标签实现文件下载
let link = document.createElement('a')
link.download = fileName
link.style.display = 'none'
link.href = res.data.msg
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} else {
ElMessage({
message: '该文件不存在',
type: 'warning',
})
}
}
最后问题就解决了,希望可以帮到你,如果有更好的方法欢迎补充讨论