|
|
|
<template>
|
|
|
|
<div class="flex justify-center items-center size-full">
|
|
|
|
<EdfsWrap title="固件上传" style="width: 50%; height: 50%">
|
|
|
|
<el-upload v-model:fileList="fileList" v-loading="loading" element-loading-text="上传中..." drag action=""
|
|
|
|
accept=".tar.gz" :limit="1" :on-exceed="handleExceed" :auto-upload="false" ref="uploadRef"
|
|
|
|
class="h-[calc(100%-30px)] w-full">
|
|
|
|
<div class="i-line-md:cloud-alt-upload-loop text-20px mx-auto"></div>
|
|
|
|
<div class="text">拖拽文件或者 <em>点击上传</em></div>
|
|
|
|
<template #tip v-if="!fileList.length">
|
|
|
|
<div class="el-upload__tip">上传限制一个文件,新文件会覆盖旧文件</div>
|
|
|
|
</template>
|
|
|
|
</el-upload>
|
|
|
|
<div class="flex justify-center">
|
|
|
|
<el-button type="primary" @click="onSave" v-show="fileList.length && !loading">确定上传</el-button>
|
|
|
|
<el-button type="info" v-show="loading" @click="onClone">取消上传</el-button>
|
|
|
|
</div>
|
|
|
|
</EdfsWrap>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { uploadFirmwareFile } from '@/api/module/firmware'
|
|
|
|
import { useMessage } from '@/composables/useMessage'
|
|
|
|
import type {
|
|
|
|
UploadInstance,
|
|
|
|
UploadProps,
|
|
|
|
UploadRawFile,
|
|
|
|
UploadUserFile,
|
|
|
|
} from 'element-plus'
|
|
|
|
|
|
|
|
const message = useMessage()
|
|
|
|
|
|
|
|
const fileList = ref<UploadUserFile[]>([])
|
|
|
|
|
|
|
|
const upload = ref<UploadInstance>()
|
|
|
|
const handleExceed: UploadProps['onExceed'] = files => {
|
|
|
|
upload.value!.clearFiles()
|
|
|
|
const file = files[0] as UploadRawFile
|
|
|
|
|
|
|
|
upload.value!.handleStart(file)
|
|
|
|
}
|
|
|
|
|
|
|
|
const uploadRef = ref<UploadInstance>()
|
|
|
|
|
|
|
|
const beforeAvatarUpload: UploadProps['beforeUpload'] = rawFile => {
|
|
|
|
const accept = ['.zip', '.tar', '.tar.gz']
|
|
|
|
const fileTypes = rawFile.name.substring(rawFile.name.indexOf('.'))
|
|
|
|
|
|
|
|
if (!accept.includes(fileTypes)) {
|
|
|
|
message.error('请上传 tar.gz 文件')
|
|
|
|
uploadRef.value?.clearFiles?.()
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
function validate() {
|
|
|
|
if (!fileList.value.length) {
|
|
|
|
message.error('请上传文件')
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return beforeAvatarUpload(fileList.value[0].raw!)
|
|
|
|
}
|
|
|
|
const loading = ref(false)
|
|
|
|
const abortController = ref<AbortController>()
|
|
|
|
|
|
|
|
async function onSave() {
|
|
|
|
if (loading.value) {
|
|
|
|
message.error('文件正在上传中,请稍后')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (!validate()) return
|
|
|
|
loading.value = true
|
|
|
|
abortController.value = new AbortController()
|
|
|
|
const data = new FormData()
|
|
|
|
data.append('file', fileList.value[0].raw as File)
|
|
|
|
const userFile = fileList.value[0] as UploadUserFile
|
|
|
|
const res = await uploadFirmwareFile(data, abortController.value)
|
|
|
|
if (res.code === 0) {
|
|
|
|
message.success('上传成功')
|
|
|
|
} else {
|
|
|
|
message.error('上传失败')
|
|
|
|
}
|
|
|
|
// const file = userFile.raw as File
|
|
|
|
// const fileName = userFile.name
|
|
|
|
|
|
|
|
// const chunkList = await createChunksFromFileData(file, 1024)
|
|
|
|
|
|
|
|
// try {
|
|
|
|
// for (const chunk of chunkList) {
|
|
|
|
// const { chunkData, offset } = chunk
|
|
|
|
// const res = await uploadSftpFile(
|
|
|
|
// {
|
|
|
|
// fileName,
|
|
|
|
// chunk: chunkData,
|
|
|
|
// targetPath: props.curPath,
|
|
|
|
// offset,
|
|
|
|
// },
|
|
|
|
// abortController.value
|
|
|
|
// )
|
|
|
|
|
|
|
|
// if (res.code === -777) {
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if (![200, 0].includes(res.code)) {
|
|
|
|
// // 如果是手动取消的请求,不提示错误
|
|
|
|
// message.error(`上传失败,${res.msg}`)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// } catch (error) {
|
|
|
|
// message.error('上传失败')
|
|
|
|
// }
|
|
|
|
abortController.value = undefined
|
|
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
|
|
onClone()
|
|
|
|
}
|
|
|
|
|
|
|
|
async function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const reader = new FileReader()
|
|
|
|
reader.readAsArrayBuffer(file)
|
|
|
|
reader.onload = () => resolve(reader.result as ArrayBuffer)
|
|
|
|
reader.onerror = reject
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createChunksFromFileData(file: File, chunkSize: number) {
|
|
|
|
const fileData = await readFileAsArrayBuffer(file)
|
|
|
|
|
|
|
|
const chunkList: { offset: number; chunkIndex: number; chunkData: Uint8Array }[] = []
|
|
|
|
let offset = 0
|
|
|
|
let chunkIndex = 0
|
|
|
|
|
|
|
|
while (offset < fileData.byteLength) {
|
|
|
|
const chunkData = new Uint8Array(fileData.slice(offset, offset + chunkSize))
|
|
|
|
chunkList.push({
|
|
|
|
offset,
|
|
|
|
chunkIndex,
|
|
|
|
chunkData,
|
|
|
|
})
|
|
|
|
|
|
|
|
offset += chunkSize
|
|
|
|
chunkIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
return chunkList
|
|
|
|
}
|
|
|
|
|
|
|
|
function clearData() {
|
|
|
|
fileList.value = []
|
|
|
|
}
|
|
|
|
function onClone() {
|
|
|
|
if (loading.value) {
|
|
|
|
abortController.value?.abort()
|
|
|
|
loading.value = false
|
|
|
|
}
|
|
|
|
clearData()
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
:deep(.el-upload) {
|
|
|
|
height: calc(100% - 50px);
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
:deep(.el-upload-dragger) {
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
</style>
|