Skip to content

upload components

supdger edited this page Oct 3, 2026 · 1 revision

文件上传组件

面向在 SandAdmin / Sand Core 管理端开发插件表单的 Vue 3 开发者。组件由前端自动注册,在已配置上传存储、已登录且具有相应权限的宿主环境中使用。普通附件用 sa-file-upload;需要分片进度和手动开始上传时用 sa-chunk-upload。以下是源码对应的接入参考,不代表本次已运行真实 HTTP 上传。

sa-chunk-upload 切片上传组件

一个支持大文件分片上传的 Vue 3 组件,基于 Element Plus 的 el-upload 封装。

功能特性

  • ✅ 分片上传: 自动将大文件切分成小块上传,支持自定义分片大小
  • ✅ MD5 标识: 自动计算文件 MD5,随分片请求发送作为文件标识
  • ✅ 进度跟踪: 实时显示上传进度、当前分片、上传速度
  • ✅ 失败重试: 上传失败后可重试,当前重试从首个分片重新开始,不承诺断点续传或刷新续传
  • ✅ 取消上传: 可随时取消正在进行的上传任务
  • ✅ 拖拽上传: 支持拖拽文件到指定区域上传
  • ✅ 文件验证: 支持文件类型和大小验证
  • ✅ 并发控制: 串行上传多个文件,避免服务器压力过大

Props 参数

参数 说明 类型 默认值
modelValue v-model 绑定值,文件 URL string | string[] []
multiple 是否支持多选文件 boolean false
limit 最大上传文件数量 number 1
maxSize 单个文件最大大小(MB) number 1024
chunkSize 分片大小(MB) number 5
accept 接受的文件类型 string '*'
acceptHint 文件类型提示文本 string ''
disabled 是否禁用 boolean false
drag 是否启用拖拽上传 boolean true
buttonText 按钮文本 string '选择文件'
autoUpload 是否自动上传 boolean false

Events 事件

事件名 说明 回调参数
update:modelValue 文件 URL 更新 (value: string | string[])
success 上传成功 (response: any)
error 上传失败 (error: any)
progress 上传进度更新 (progress: number)

基本用法

<template>
  <sa-chunk-upload v-model="fileUrl" />
</template>

<script setup>
import { ref } from 'vue'

const fileUrl = ref('')
</script>

高级用法

1. 大视频文件上传

<template>
  <sa-chunk-upload
    v-model="videoUrl"
    accept="video/*"
    accept-hint="MP4、AVI、MOV"
    :max-size="2048"
    :chunk-size="10"
    :drag="true"
    @success="handleSuccess"
    @progress="handleProgress"
  />
</template>

<script setup>
import { ref } from 'vue'

const videoUrl = ref('')

const handleSuccess = (response) => {
  console.log('上传成功:', response)
}

const handleProgress = (progress) => {
  console.log('上传进度:', progress + '%')
}
</script>

2. 多文件上传

<template>
  <sa-chunk-upload
    v-model="fileUrls"
    :multiple="true"
    :limit="5"
    :chunk-size="5"
  />
</template>

<script setup>
import { ref } from 'vue'

const fileUrls = ref([])
</script>

3. 限制文件类型

<template>
  <!-- 只允许上传压缩文件 -->
  <sa-chunk-upload
    v-model="zipUrl"
    accept=".zip,.rar,.7z"
    accept-hint="ZIP、RAR、7Z"
    :max-size="500"
  />

  <!-- 只允许上传 PDF -->
  <sa-chunk-upload
    v-model="pdfUrl"
    accept=".pdf"
    accept-hint="PDF"
  />
</template>

4. 自动上传模式

<template>
  <sa-chunk-upload
    v-model="fileUrl"
    :auto-upload="true"
  />
</template>

5. 手动控制上传

<template>
  <sa-chunk-upload
    v-model="fileUrl"
    :auto-upload="false"
  />
  <!-- 组件会自动显示"开始上传"按钮 -->
</template>

后端接口要求

组件会调用 chunkUpload API,每个分片上传时会发送以下参数:

{
  file: Blob,           // 当前分片
  ext: string,          // 文件扩展名
  size: string,         // 整个文件字节数
  type: string,         // MIME 类型
  hash: string,         // 整个文件的 MD5
  index: string,        // 当前分片索引,从 0 开始
  total: string,        // 分片总数
  name: string          // 原文件名
}

当前 API 是 /core/system/chunkUpload,以 FormData 发送以上字段;数字字段通过 toString() 发送。宿主已有 ChunkUploadService 负责接收、合并和记录附件,插件接入者无需重复实现服务。组件从 API 解包后的结果读取 url;最终成功后更新 v-model,并触发 success({ url, hash })。

后端实现建议

  1. 接收分片: 根据 hash 和 index 保存分片
  2. 合并文件: 当接收到所有分片后(index === total - 1),合并所有分片
  3. 返回 URL: 合并完成后返回文件访问 URL
  4. 失败重试: 当前组件从首个分片重新开始;已有后端分片检查能力不代表当前组件实现了断点续传

示例后端响应

{
  "code": 200,
  "data": {
    "url": "/uploads/abc123def456/example.mp4",
    "hash": "abc123def456"
  },
  "message": "上传成功"
}

工作原理

  1. 文件选择: 用户选择文件后,组件计算文件需要分成多少片
  2. MD5 计算: 计算整个文件的 MD5 哈希值,作为分片文件标识
  3. 分片上传: 按顺序上传每个分片,实时更新进度
  4. 进度显示: 显示当前分片、总分片数、上传速度
  5. 完成处理: 所有分片上传完成后,更新 v-model 值

性能优化建议

  1. 分片大小:

    • 网络较好: 可设置 10-20MB
    • 网络一般: 建议 5-10MB
    • 网络较差: 建议 2-5MB
  2. 文件大小限制: 根据实际需求设置合理的 maxSize

  3. 并发控制: 组件默认串行上传文件,避免同时上传多个大文件

注意事项

  1. 当前前端清单已有 spark-md5 和 @types/spark-md5;按宿主安装流程安装依赖即可。
  2. 服务端上传配置须允许目标扩展名,上传目录须可写;使用已登录且有上传权限的宿主环境。
  3. MD5 用于标识文件;本说明不承诺后端会重新校验分片内容。
  4. 大文件上传时注意服务器及代理的请求体和超时限制。
  5. 成功应看到文件地址写入 v-model;若收到格式拒绝,核对服务端允许的扩展名;若返回“上传完成但未返回文件地址”,应查看上传 API 响应和宿主日志,再重试。

单独接入时的依赖

pnpm add spark-md5
pnpm add -D @types/spark-md5

sa-file-upload 普通文件上传

使用 /core/system/uploadFile。默认单选、最多一个文件、大小上限 10 MB,不启用拖拽;选中文件后立即上传。多选需要同时设置 multiple 和 limit。accept 是客户端筛选,服务端允许的扩展名仍由宿主配置决定。

以下示例保留单文件、多文件、拖拽、PDF/Word、压缩文件五种使用方式。成功后 v-model 获得地址(多文件为地址数组);上传失败时组件显示错误,确认格式、大小和权限后重新选择上传。

<!--
  sa-file-upload 组件使用示例

  基本用法:
  <sa-file-upload v-model="fileUrl" />

  多文件上传:
  <sa-file-upload v-model="fileUrls" :multiple="true" :limit="5" />

  拖拽上传:
  <sa-file-upload v-model="fileUrl" :drag="true" />

  限制文件类型:
  <sa-file-upload
    v-model="fileUrl"
    accept=".pdf,.doc,.docx"
    accept-hint="PDF、Word"
  />
-->

<template>
  <div class="demo-container">
    <h2>文件上传组件示例</h2>

    <!-- 示例1: 基本用法 -->
    <el-card header="基本用法" class="demo-card">
      <sa-file-upload v-model="singleFile" />
      <div class="result">当前文件: {{ singleFile }}</div>
    </el-card>

    <!-- 示例2: 多文件上传 -->
    <el-card header="多文件上传" class="demo-card">
      <sa-file-upload
        v-model="multipleFiles"
        :multiple="true"
        :limit="5"
      />
      <div class="result">当前文件: {{ multipleFiles }}</div>
    </el-card>

    <!-- 示例3: 拖拽上传 -->
    <el-card header="拖拽上传" class="demo-card">
      <sa-file-upload
        v-model="dragFile"
        :drag="true"
        button-text="点击或拖拽上传"
      />
      <div class="result">当前文件: {{ dragFile }}</div>
    </el-card>

    <!-- 示例4: 限制文件类型 - PDF/Word -->
    <el-card header="限制文件类型 (PDF、Word)" class="demo-card">
      <sa-file-upload
        v-model="docFile"
        accept=".pdf,.doc,.docx"
        accept-hint="PDF、Word"
        :max-size="20"
      />
      <div class="result">当前文件: {{ docFile }}</div>
    </el-card>

    <!-- 示例5: 限制文件类型 - Excel -->
    <el-card header="限制文件类型 (Excel)" class="demo-card">
      <sa-file-upload
        v-model="excelFile"
        accept=".xls,.xlsx"
        accept-hint="Excel"
      />
      <div class="result">当前文件: {{ excelFile }}</div>
    </el-card>

    <!-- 示例6: 压缩文件 -->
    <el-card header="压缩文件上传" class="demo-card">
      <sa-file-upload
        v-model="zipFile"
        accept=".zip,.rar,.7z"
        accept-hint="ZIP、RAR、7Z"
        :max-size="50"
        :drag="true"
      />
      <div class="result">当前文件: {{ zipFile }}</div>
    </el-card>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const singleFile = ref('')
const multipleFiles = ref<string[]>([])
const dragFile = ref('')
const docFile = ref('')
const excelFile = ref('')
const zipFile = ref('')
</script>

<style scoped lang="scss">
.demo-container {
  padding: 20px;

  h2 {
    margin-bottom: 20px;
  }

  .demo-card {
    margin-bottom: 20px;

    .result {
      margin-top: 15px;
      padding: 10px;
      background-color: #f5f7fa;
      border-radius: 4px;
      font-size: 12px;
      color: #606266;
      word-break: break-all;
    }
  }
}
</style>

返回插件开发与发布。

Clone this wiki locally