Softhub软件下载站实战开发(十三):软件管理前端分片上传实现

发布于:2025-07-06 ⋅ 阅读:(13) ⋅ 点赞:(0)

Softhub软件下载站实战开发(十三):软件管理前端分片上传实现 🚀

前言

在之前文章中,我们实现了软件分片上传的后端接口。本文将聚焦于前端分片上传的实现,详细介绍如何使用Vue3和Element Plus构建一个高效、稳定的文件上传组件,并与后端分片上传接口进行对接。

前端分片上传架构设计 🏗️

前端分片上传主要分为三个步骤:

  1. 文件选择与分片准备
  2. 分片上传过程
  3. 上传完成与合并
用户选择文件
初始化上传
分片文件
上传分片
所有分片上传完成?
合并分片
上传完成

核心组件实现 💻

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. 分片上传逻辑实现

在确认上传后,组件会执行以下分片上传流程:

用户界面 前端代码 后端API 点击上传按钮 初始化上传(文件信息) 返回uploadId 分割文件为5MB分片 上传分片n 返回结果 更新进度条 loop [分片上传] 合并分片请求 返回最终结果 显示上传成功 用户界面 前端代码 后端API

关键代码实现:

// 创建文件分片
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问题
  • 提供完善的进度反馈和错误处理

这套方案不仅适用于软件下载站,也可以应用于任何需要大文件上传的场景,如视频平台、云存储服务等。


网站公告

今日签到

点亮在社区的每一天
去签到