文章目录
Softhub软件下载站实战开发(十三):软件管理前端分片上传实现 🚀
前言
在之前文章中,我们实现了软件分片上传的后端接口。本文将聚焦于前端分片上传的实现,详细介绍如何使用Vue3和Element Plus构建一个高效、稳定的文件上传组件,并与后端分片上传接口进行对接。
前端分片上传架构设计 🏗️
前端分片上传主要分为三个步骤:
- 文件选择与分片准备
- 分片上传过程
- 上传完成与合并
核心组件实现 💻
1. 上传资源组件 (uploadResource.vue
)
这是用户上传文件的入口组件,主要功能是文件选择和初步信息收集。
<template>
<el-upload
class="upload-demo"
drag
:auto-upload="false"
:on-change="handleFileChange"
:limit="1"
:file-list="fileList"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
</el-upload>
</template>
<script setup>
const handleFileChange = (file) => {
// 从文件名中自动提取版本号
const versionMatch = file.name.match(/[vV]?(\d+\.\d+\.\d+)/);
formData.version = versionMatch ? versionMatch[1] : '';
// 设置默认资源名称
formData.resourceName = file.name;
formData.file = file.raw;
}
</script>
2. 分片上传逻辑实现
在确认上传后,组件会执行以下分片上传流程:
关键代码实现:
// 创建文件分片
const createFileChunks = (file: File) => {
const chunks = [];
let cur = 0;
while (cur < file.size) {
chunks.push({
index: chunks.length,
file: file.slice(cur, cur + CHUNK_SIZE)
});
cur += CHUNK_SIZE;
}
return chunks;
};
// 上传分片
const uploadChunkFile = async (chunk, uploadId) => {
const formDataObj = new FormData();
formDataObj.append('file', chunk.file);
formDataObj.append('uploadId', uploadId);
formDataObj.append('chunkIndex', String(chunk.index));
formDataObj.append('fileName', formData.file!.name);
formDataObj.append('softwareId', String(props.softwareId));
await uploadChunk(formDataObj);
};
// 完整上传流程
const onConfirm = async () => {
// 初始化上传
const initResponse = await initChunkUpload({
softwareId: props.softwareId,
fileName: formData.file.name,
fileSize: formData.file.size,
chunkSize: CHUNK_SIZE
});
const uploadId = initResponse.data.data.uploadId;
// 创建分片
const chunks = createFileChunks(formData.file);
// 显示进度条
const loadingInstance = ElLoading.service({
text: '正在上传文件... 0%'
});
// 上传所有分片
for (let i = 0; i < chunks.length; i++) {
await uploadChunkFile(chunks[i], uploadId);
const progress = Math.round(((i + 1) / chunks.length) * 100);
loadingInstance.setText(`正在上传文件... ${progress}%`);
}
// 合并分片
await mergeChunks({
uploadId,
softwareId: props.softwareId,
resourceName: formData.resourceName,
version: formData.version,
remark: formData.remark
});
loadingInstance.close();
ElMessage.success('上传成功');
};
3. 资源管理组件 (editResource.vue
)
这个组件提供了已上传资源的管理界面,包括:
- 资源列表展示
- 设置默认版本
- 资源删除
- 资源下载
<template>
<el-table :data="tableData.data">
<el-table-column prop="resourceName" label="软件名称"/>
<el-table-column prop="size" label="资源大小">
<template #default="scope">
<FormattedSize :sizeInBytes="scope.row.size" />
</template>
</el-table-column>
<el-table-column prop="default" label="是否默认">
<template #default="scope">
<el-switch
v-model="scope.row.default"
@click="onSwitchDefault(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="onRowDownload(scope.row)">下载</el-button>
<el-button @click="openManageResourceDialog(scope.row, 'edit')">修改</el-button>
<el-button @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
文件大小格式化组件 📏
为了方便显示文件大小,我们创建了一个专用的格式化组件:
<template>
{{ formattedSize }}
</template>
<script setup>
const formattedSize = computed(() => {
const bytes = sizeInBytes.value;
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
});
</script>
大整数处理方案 🔢
由于我们使用雪花算法生成ID,前端需要特殊处理大整数:
/**
* 将大整数转换为字符串,避免精度丢失
* @param value 要转换的值
* @returns 字符串形式的大整数
*/
export function toBigIntString(value) {
if (value === null || value === undefined) {
return '0';
}
// 如果已经是字符串,直接返回
if (typeof value === 'string') {
return value;
}
// 如果是数字,检查是否超过安全整数范围
if (typeof value === 'number') {
if (Number.isSafeInteger(value)) {
return value.toString();
} else {
// 超过安全整数范围,使用BigInt
try {
return BigInt(value).toString();
} catch (error) {
console.error('BigInt转换失败:', error);
return value.toString();
}
}
}
// 其他类型,尝试转换为字符串
try {
return BigInt(value).toString();
} catch (error) {
console.error('BigInt转换失败:', error);
return String(value);
}
}
总结 🎯
通过本文的实现,我们完成了Softhub软件下载站的前端分片上传功能。关键点包括:
- 使用Element Plus构建友好的上传界面
- 实现高效的分片上传逻辑
- 处理大整数ID问题
- 提供完善的进度反馈和错误处理
这套方案不仅适用于软件下载站,也可以应用于任何需要大文件上传的场景,如视频平台、云存储服务等。