跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端

Vue 3 与 React 文件上传实战

详细讲解了使用 Vue 3 和 React 进行文件上传的实现方法。内容包括原生 HTML 输入框配合 FormData 的方式,以及基于 Element Plus 和 Ant Design 组件库的封装用法。涵盖了单文件与多文件上传、拖拽上传、手动上传前的文件校验、上传进度监听等核心场景。最后通过对比表格总结了各方案的优劣,为实际开发提供参考。

星落发布于 2026/4/5更新于 2026/9/871 浏览
Vue 3 与 React 文件上传实战

Vue 3 + React 实现单文件和多文件上传

文件上传是前端开发中的常见功能,涵盖限制文件大小、显示上传进度、支持拖拽及文件类型校验等场景。

一、Vue 3 原生实现

1.1 单文件上传

使用原生方式实现单文件上传。

<template>
  <div>
    <!-- 文件选择框 -->
    <input type="file" @change="handleFile" />
    <!-- 上传按钮 -->
    <button @click="upload" :disabled="!file">上传</button>
    <!-- 预览文件名 -->
    <p v-if="file">已选择:{{ file.name }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const file = ref(null)

// 监听文件选择事件
const handleFile = (e) => {
  file.value = e.target.files[0]
}

// 上传文件到服务器
const upload = async () => {
  if (!file.value) return
  const formData = new FormData()
  formData.append('file', file.value)
  await fetch('/api/upload', {
    method: 'POST',
    body: formData
  })
}
</script>

注意细节:

  • e.target.files[0] 获取第一个选中的文件,如需多选需添加 multiple 属性。
  • FormData 对象会自动设置 Content-Type 边界值。

1.2 多文件上传

改动不大,主要处理多个文件列表。

<template>
  <div>
    <input type="file" multiple @change="handleFiles" />
    <button @click="upload" :disabled="!files.length">批量上传</button>
    <ul>
      <li v-for="f in files" :key="f.name">{{ f.name }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const files = ref([])

// 获取选中的多个文件
const handleFiles = (e) => {
  files.value = Array.from(e.target.files)
}

// 批量上传多个文件
const upload = async () => {
  if (!files.value.length) return
  const formData = new FormData()
  // 注意:这里要用相同的字段名 'files',后端才能用 List/MultipartFile[] 接收
  files.value.forEach(f => formData.append('files', f))
  await fetch('/api/upload/multiple', {
    method: 'POST',
    body: formData
  })
}
</script>

注意后端接收时,前端 append 的字段名需与后端参数名一致。

二、Vue 3 + Element Plus 实现

使用组件库可简化样式和交互逻辑。

2.1 单文件上传

<template>
  <el-upload action="/api/upload" :on-success="handleSuccess" :on-error="handleError" :show-file-list="false">
    <el-button type="primary">选择文件</el-button>
  </el-upload>
</template>

<script setup>
// 上传成功回调
const handleSuccess = (res) => {
  ElMessage.success('上传成功')
}

// 上传失败回调
const handleError = () => {
  ElMessage.error('上传失败')
}
</script>

action 为必填项。:show-file-list="false" 用于隐藏已上传文件列表。

2.2 多文件上传

<template>
  <el-upload
    action="/api/upload"
    :file-list="fileList"
    :on-success="handleSuccess"
    :on-remove="handleRemove"
    :on-exceed="handleExceed"
    :limit="5"
    multiple
  >
    <el-button type="primary">选择文件</el-button>
    <template #tip>
      <div>支持 jpg、png、pdf 格式,单个文件不超过 10MB</div>
    </template>
  </el-upload>
</template>

<script setup>
import { ref } from 'vue'
const fileList = ref([])

// 文件数量超出限制了
const handleExceed = () => {
  ElMessage.warning('最多只能上传 5 个文件')
}

// 手动移除了某个文件
const handleRemove = (file) => {
  console.log('移除了:', file.name)
}

// 单个文件上传成功的回调
const handleSuccess = (res, file) => {
  ElMessage.success(`${file.name} 上传成功`)
}
</script>

2.3 拖拽上传

<template>
  <el-upload
    action="/api/upload"
    drag
    :auto-upload="false"
    :on-change="handleFileChange"
  >
    <el-icon><upload-filled /></el-icon>
    <div>将文件拖到此处,或<em>点击上传</em></div>
  </el-upload>
</template>

<script setup>
import { UploadFilled } from '@element-plus/icons-vue'

// 文件被添加或者被移除的时候触发
const handleFileChange = (file) => {
  console.log('选中文件:', file.name)
}
</script>

:auto-upload="false" 关闭自动上传,通常配合用户确认操作。

2.4 手动上传(上传前校验)

<template>
  <el-upload
    ref="uploadRef"
    action="/api/upload"
    :auto-upload="false"
    :before-upload="beforeUpload"
    :on-change="handleChange"
  >
    <el-button type="success">选取文件</el-button>
    <template #tip>
      <el-button type="primary" @click="submitUpload">开始上传</el-button>
    </template>
  </el-upload>
</template>

<script setup>
import { ref } from 'vue'
const uploadRef = ref(null)

// 上传前的校验,返回 false 可以取消上传
const beforeUpload = (rawFile) => {
  // 检查文件大小,10MB
  const isLt10M = rawFile.size / 1024 / 1024 < 10
  if (!isLt10M) {
    ElMessage.error('文件大小不能超过 10MB')
    return false
  }
  return true
}

// 文件被添加或移除时的回调
const handleChange = (file) => {
  console.log('当前文件:', file.name)
}

// 手动调用上传
const submitUpload = () => {
  uploadRef.value.submit()
}
</script>

beforeUpload 钩子可用于文件校验。

三、React + Ant Design 实现

3.1 单文件上传

import { Upload, message } from 'antd'

function SingleUpload() {
  // 上传成功
  const handleSuccess = (res) => {
    message.success('上传成功')
  }

  // 上传失败
  const handleError = () => {
    message.error('上传失败')
  }

  return (
    <Upload
      action="/api/upload"
      onSuccess={handleSuccess}
      onError={handleError}
      showUploadList={false}
    >
      <button>选择文件</button>
    </Upload>
  )
}

3.2 多文件上传

import { Upload, message, Button } from 'antd'

function MultiUpload() {
  // 超出数量限制了
  const handleExceed = () => {
    message.warning('最多只能上传 5 个文件')
  }

  // 文件状态变化的时候
  const handleChange = (info) => {
    if (info.fileList.length > 5) {
      handleExceed()
      return
    }
    info.fileList.forEach(file => {
      if (file.status === 'done') {
        message.success(`${file.name} 上传成功`)
      } else if (file.status === 'error') {
        message.error(`${file.name} 上传失败`)
      }
    })
  }

  return (
    <Upload
      action="/api/upload"
      multiple
      maxCount={5}
      onChange={handleChange}
    >
      <Button>选择文件</Button>
    </Upload>
  )
}

maxCount 属性可直接限制最大数量。

3.3 拖拽上传

import { Upload, message } from 'antd'
import { InboxOutlined } from '@ant-design/icons'

function DragUpload() {
  // 拖拽释放后的回调
  const handleDrop = ({ fileList }) => {
    console.log('拖拽进来的文件:', fileList)
  }

  return (
    <Upload.Dragger
      action="/api/upload"
      multiple
      drag
      onDrop={handleDrop}
    >
      <p className="ant-upload-drag-icon">
        <InboxOutlined />
      </p>
      <p>点击或拖拽文件到此区域上传</p>
    </Upload.Dragger>
  )
}

3.4 手动上传

import { Upload, Button, message } from 'antd'
import { UploadOutlined } from '@ant-design/icons'
import { useState } from 'react'

function ManualUpload() {
  const [fileList, setFileList] = useState([])

  // 选完文件后,不触发上传,只更新列表
  const handleChange = ({ fileList: newFileList }) => {
    setFileList(newFileList)
  }

  // 手动触上传
  const handleUpload = () => {
    if (fileList.length === 0) {
      message.warning('请先选择文件')
      return
    }
    const formData = new FormData()
    fileList.forEach(file => {
      // originFileObj 才是原始的 File 对象
      formData.append('files', file.originFileObj)
    })
    fetch('/api/upload', {
      method: 'POST',
      body: formData
    }).then(() => {
      message.success('上传成功')
      setFileList([])
    })
  }

  return (
    <div>
      <Upload
        fileList={fileList}
        onChange={handleChange}
        beforeUpload={() => false}
        multiple
      >
        <Button icon={<UploadOutlined />}>选择文件</Button>
      </Upload>
      <Button
        type="primary"
        onClick={handleUpload}
        style={{ marginTop: 16 }}
      >
        开始上传
      </Button>
    </div>
  )
}

注意:fileList 中的 originFileObj 才是原始 File 实例。

四、进度监听

Vue 3 进度监听

Element Plus 未直接提供进度回调,需使用 XMLHttpRequest 或 axios。

<script setup>
import { ref } from 'vue'
const progress = ref(0)
const uploading = ref(false)

const uploadWithProgress = () => {
  const fileInput = document.querySelector('input[type="file"]')
  const file = fileInput.files[0]
  if (!file) return

  const formData = new FormData()
  formData.append('file', file)

  const xhr = new XMLHttpRequest()
  // 监听上传进度
  xhr.upload.onprogress = (e) => {
    if (e.lengthComputable) {
      const percent = Math.round((e.loaded / e.total) * 100)
      progress.value = percent
      console.log(`上传进度:${percent}%`)
    }
  }

  // 上传完成
  xhr.onload = () => {
    if (xhr.status === 200) {
      ElMessage.success('上传成功')
    }
    uploading.value = false
  }

  xhr.open('POST', '/api/upload')
  xhr.send(formData)
  uploading.value = true
}
</script>

<template>
  <div>
    <input type="file" @change="handleFile" />
    <button @click="uploadWithProgress" :disabled="uploading">
      {{ uploading ? `上传中 ${progress}%` : '上传' }}
    </button>
    <el-progress :percentage="progress" v-if="uploading" />
  </div>
</template>

若使用 axios:

import axios from 'axios'

const uploadWithAxios = (file) => {
  const formData = new FormData()
  formData.append('file', file)
  axios.post('/api/upload', formData, {
    onUploadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
      console.log(`进度:${percent}%`)
    }
  })
}

React + Ant Design 进度属性

Ant Design 的 Upload 组件支持 onProgress。

<Upload
  action="/api/upload"
  withCredentials
  onProgress={(event, file) => {
    // event.percent 是 0-100 的数字
    const percent = Math.round(event.percent)
    console.log(`当前进度:${percent}%`)
  }}
>
  <Button>上传</Button>
</Upload>

withCredentials 用于跨域上传时携带凭证。

五、总结对比

功能Vue 原生Vue + Element PlusReact 原生React + Ant Design
单文件上传input + fetchel-uploadinput + fetchUpload
多文件上传multiple + forEachel-upload + multiplemultiple + forEachUpload + multiple
拖拽上传drag eventsel-upload + dragdrag eventsUpload.Dragger
进度监听xhr.onprogress需二次封装xhr.onprogressonProgress
文件限制手动校验:limit/:before-upload手动校验maxCount/beforeUpload
手动上传控制时机:auto-upload="false"控制时机beforeUpload={() => false}
上传样式自己写 CSS开箱即用自己写 CSS开箱即用

建议根据项目技术栈选择方案。若已使用组件库,直接使用其 Upload 组件最为便捷;原生开发则依赖少、灵活性高。

目录

  1. Vue 3 + React 实现单文件和多文件上传
  2. 一、Vue 3 原生实现
  3. 1.1 单文件上传
  4. 1.2 多文件上传
  5. 二、Vue 3 + Element Plus 实现
  6. 2.1 单文件上传
  7. 2.2 多文件上传
  8. 2.3 拖拽上传
  9. 2.4 手动上传(上传前校验)
  10. 三、React + Ant Design 实现
  11. 3.1 单文件上传
  12. 3.2 多文件上传
  13. 3.3 拖拽上传
  14. 3.4 手动上传
  15. 四、进度监听
  16. Vue 3 进度监听
  17. React + Ant Design 进度属性
  18. 五、总结对比

更多推荐文章

查看全部
  • OpenClaw 浏览器自动化实战:让 AI 模拟人类操作网页
  • 从神经元基本原理理解 LLM 智能涌现
  • Web 自动化测试实战:基于 Python+Selenium 的博客系统测试流程
  • Java 基于百度天气 API 实现海外城市实时天气获取
  • 成为黑客的 12 个基本步骤与核心技能指南
  • 2026 年 Python 发展局势:AI 时代的通用基础设施语言
  • 国产开源视频生成模型 CogVideoX 2B 发布,支持单卡推理与微调
  • C++ 标准库 reverse 函数详解与实战
  • Vue 3 开发实战:10 个提升效率的核心技巧
  • ROS2 使用功能包组织 Python 节点
  • OpenClaw 多智能体路由实战:飞书多机器人配置指南
  • 2026 年 3 月 GESP C++ 一级真题及解析:数字替换
  • 毕业论文降低 AI 检测率的原理与实操指南
  • LangChain 开发入门教程:构建 LLM 驱动应用指南
  • 基于 SSH 加密安全连接远程 OpenClaw 网关的方法
  • HTTP 代理 IP 地址列表
  • 分布式文件系统 HDFS 编程实践与常用命令详解
  • Spring AOP 注解实现详解
  • Debian 12 安装 OpenJDK 17 及配置 Tomcat 10.1.26 指定 JDK
  • AI 产品经理成长指南:核心能力与技能路径

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online