前端文件上传实战

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

文件上传这个功能,说简单也简单,说复杂也真是能玩出不少花样。

记得我第一次写文件上传的时候,就是一个 <input type="file"> 配上 FormData,完事儿。但后来需求就开始不对劲了——要限制文件大小、要显示上传进度、要支持拖拽、要做文件类型校验…

得,那咱就一步步来,把常见的文件上传场景都覆盖一遍。

一、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 的字段名和后端的 @RequestParam("files") 或者 @ModelAttribute("files") 对得上,不然收不到文件。

二、Vue 3 + Element Plus 实现

原生写是能写,但样式得自己调,交互效果也得自己写。

说实话,一般项目我都会直接用组件库,省心。Element Plus 的 Upload 组件功能挺完善的,该有的都有。

2.1 单文件上传

用 Element Plus 写,代码确实少很多。

<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 多文件上传

多文件其实就是加个 multiple 属性,再加个 limit 限制数量。

<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> 

on-exceed 这个回调挺实用的,用户选了 10 个文件,你只让传 5 个,这时候就得提示一下用户。

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 这个钩子非常有用,返回 false 就能阻止上传。用它来做文件校验,再合适不过了。

三、React + Ant Design 实现

说完 Vue,再来看看 React 那边。Ant Design 的 Upload 组件也很好用。

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> ) } 

Ant Design 的写法跟 Element Plus 有点区别,它是 props 风格的回调,不过用起来都挺直观的。

3.2 多文件上传

import { Upload, message, Button } from 'antd' function MultiUpload() { // 超出数量限制了 const handleExceed = () => { message.warning('最多只能上传 5 个文件') } // 文件状态变化的时候 const handleChange = (info) => { // 如果手动删了一些文件,导致数量又合规了,这里其实不用特别处理 // Ant Design 会自动处理上传列表 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 拖拽上传

Ant Design 专门提供了 Dragger 组件来做拖拽。

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> ) } 

拖拽区域的样式是封装好的,不用自己写 CSS,挺好的。

3.4 手动上传

Ant Design 默认是选中文件就上传,如果想手动上传,需要做点配置。

import { Upload, Button, message } from 'antd' import { UploadOutlined } from '@ant-design/icons' 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} // 返回 false 表示阻止自动上传 beforeUpload={() => false} multiple > <Button icon={<UploadOutlined />}>选择文件</Button> </Upload> <Button type="primary" onClick={handleUpload} style={{ marginTop: 16 }}> 开始上传 </Button> </div> ) } 

这里有个小坑:fileList 里的每个 file 对象,它的 originFileObj 才是原始的 File 实例。直接用 file 可能会出问题。

四、进度监听

上传大文件的时候,用户肯定想知道上传进度,不然还以为页面卡死了。

Vue 3 进度监听

Element Plus 的 el-upload 组件没有直接提供进度回调,需要用原生 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,就更简单了,axios 封装了进度事件。

import axios from'axios'constuploadWithAxios=(file)=>{const formData =newFormData() 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 // 带上 cookie 等凭证 onProgress={(event, file) => { // event.percent 是 0-100 的数字 const percent = Math.round(event.percent) console.log(`当前进度: ${percent}%`) }} > <Button>上传</Button> </Upload> 

withCredentials 这个属性记得加上,不然跨域上传的时候 cookie 之类的凭证不会带上。

五、总结对比

写了这么多,来张表总结一下各方案的特点:

功能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开箱即用

总的来说,如果你的项目已经用了 Element Plus 或 Ant Design,直接用它们的 Upload 组件是最省事的。如果你是原生开发,那就得自己多写点代码,但胜在依赖少、灵活性高。

文件上传这个功能,看起来简单,里面的门道还挺多的。实际项目中,根据业务需求选择合适的方案就好。

Read more

LLaMA-Factory配置文件详解:YAML参数调优指南

LLaMA-Factory配置文件详解:YAML参数调优指南 你是否还在为LLM微调时的参数配置感到困惑?是否因参数设置不当导致训练效率低下或模型效果不佳?本文将系统解析LLaMA-Factory的YAML配置文件结构,通过实际案例演示关键参数调优方法,帮助你在10分钟内掌握高效微调的配置技巧。读完本文后,你将能够独立编写优化的配置文件,解决90%的常见微调参数问题。 配置文件基础结构 LLaMA-Factory采用模块化的YAML配置系统,将微调任务划分为5个核心配置区块。这种结构设计使参数管理更清晰,也便于不同任务间的配置复用。典型的配置文件结构如下: ### model # 模型基础配置 ### method # 微调方法配置 ### dataset # 数据集处理配置 ### output # 训练输出配置 ### train # 训练过程配置 ### eval # 评估相关配置(可选) 项目中提供了大量配置示例,覆盖从基础SFT到高级RLHF的各类任务。例如: * LoRA微调示例:examples/train_lora/llama3_lora_sft.ya

AIGC已经不是未来,而是现在:2025年最值得关注的6大趋势!

AIGC已经不是未来,而是现在:2025年最值得关注的6大趋势!

过去一年,AIGC(AI 生成内容)从“概念”彻底走向“落地”。无论你是程序员、产品经理、内容创作者,甚至是业余爱好者,AIGC 已经渗透到每一个内容生产链条中,以一种“你还没准备好,它已经来了”的节奏迅速发展。 本文将带你系统了解:2025 年最热门的 AIGC 内容形态、前沿产品、典型用例,以及未来趋势。 🎥 1. 文生视频已落地:Sora 等产品引爆创意革命         当 OpenAI 推出 Sora 时,整个 AI 圈都沸腾了。         只需一句提示词,比如: "一个穿太空服的熊猫在月球上弹钢琴"         Sora 就能输出秒级电影级视频片段。光影、动作、镜头感,全部一应俱全。 🔧 技术关键词:

Llama.cpp 全实战指南:跨平台部署本地大模型的零门槛方案

【个人主页:玄同765】 大语言模型(LLM)开发工程师|中国传媒大学·数字媒体技术(智能交互与游戏设计) 深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调 技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️ 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案        「让AI交互更智能,让技术落地更高效」 欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能! 摘要 本文全面解析轻量级大模型推理框架 Llama.cpp,详细讲解其在 Windows(Winget)、Linux、macOS 三大平台的安装步骤,针对新手优化了模型获取、文件整理、可视化部署的全流程,涵盖命令行交互、OpenAI

2025终极指南:whisper.cpp跨平台语音识别部署全流程

2025终极指南:whisper.cpp跨平台语音识别部署全流程 【免费下载链接】whisper.cppOpenAI 的 Whisper 模型在 C/C++ 中的移植版本。 项目地址: https://gitcode.com/GitHub_Trending/wh/whisper.cpp 还在为语音转文字服务的网络延迟和高成本烦恼?whisper.cpp作为开源语音识别解决方案,提供了本地化部署的完美选择。本文将带你深入了解如何在不同平台上快速部署和使用这个强大的离线语音识别工具。 通过本文,你将掌握: * 多平台环境配置的一键安装方法 * 模型下载与优化的性能调优技巧 * 常见部署问题的快速解决方案 * 监控与维护的最佳实践 平台选择:找到最适合你的方案 平台类型安装难度推理速度内存占用适用场景Windows桌面⭐⭐1.2x1.1GB个人使用Linux服务器⭐⭐⭐1.5x0.9GB企业部署macOS开发⭐2.0x0.7GB移动应用Android设备⭐⭐⭐⭐0.8x0.5GB边缘计算 环境搭建:快速启动的完整步骤 基础环境准备