vuetify | vue | 文件上传组件 | file | upload | form input[type=“file“]

本文介绍如何在Vuetify中使用自定义方法实现文件上传功能,通过结合Vue.js和Django,创建一个隐藏的HTML原生输入框并利用Vue的ref属性进行操作,实现点击美观的Vuetify按钮触发文件选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好了,当你看到这句话的时候,最新的vuetify已经有了自己的Upload组件。不过想了解自定义的朋友可以继续看看

 

<hr />

今天无聊地写vuecli3听歌的时候,遇到了上传文件到Django的自我需求,然后就到vuetify的表单组件里找upload btn,发现居然没有!!!

顿时惊了个呆,要知道之前用element做操作系统课设的时候,还是挺全的。

这里偷偷吐槽,vuetify的alert真的丑......

这里分享一个解决办法,挺简单的,网上好多办法都模棱两可,而且还有外国友人在npm发布了一个组件,名字我忘了,巨无敌难用。

 

1.直接上代码

 1 <template>
 2     <v-container>
 3         <v-layout>
 4             <v-flex>
 5                 <v-btn @click="upload" depressed>
 6                     Upload Image
 7                 </v-btn>
 8                 <input type="file" id="upload" ref="upload" @change="changeimg" accept=".jpg, .jpeg, .png">
 9             </v-flex>
10         </v-layout>
11     </v-container>
12 </template>
13 
14 <script>
15 import myService from '@/services/myService'
16 
17 export default {
18     components: {
19     },
20     data () {
21       return {
24       }
25     },
26     methods: {
27         upload () {
28             let uploadbtn = this.$refs.upload
29             uploadbtn.click()
30         },
31         async changeimg (e) {
32             let formData=new FormData()
33             formData.append('file', e.target.files[0])
34             await myServiece.fun(formData)
35             console.log('send')
36         }
37     }
38   }
39 </script>
40 
41 <style scoped>
42 #upload{
43     height: 0;
44     width: 0;
45     visibility: hidden;
46 }
47 </style>

加油,我相信你能看懂,嘻嘻

算了,我还是简单说一下吧....就是创建两个btn,第一个是好看的vuetify的,另一个是html原生的input(类型是file),然后把第二个用vue的ref记录下来,以便后面调用,并且用css隐藏起来。

给第一个btn绑定upload方法,当点击时,调用第二个btn的click事件,这样就有一个错觉,你懂吧,我懒得说了...自己看代码把

最后上效果图

 

<think>我们正在处理一个Vue3文件上传的需求。根据引用,我们知道<input type="file">是文件上传的核心元素。在Vue3中,我们可以使用组合式API来处理文件上传。 步骤: 1. 在模板中创建input[type=file]元素,并监听change事件。 2. 在事件处理函数中,通过event.target.files获取用户选择的文件列表(FileList对象)。 3. 由于FileList是类数组对象,我们可以通过索引访问单个文件,或者遍历多个文件(如果允许多选)。 4. 我们可以将文件信息存储在响应式状态中,以便在界面上显示(如文件名、大小等)。 5. 如果需要上传,我们可以使用FormData对象将文件附加到表单数据中,然后通过axios或其他HTTP库发送到服务器。 注意:如果需要支持多文件上传,记得在input上添加multiple属性。 另外,根据引用[3],我们还可以将文件转换为Base64格式,这在某些场景下(如图片预览)很有用。 示例代码将包括: - 一个简单的文件选择界面 - 显示选中的文件名 - 将选中的文件通过FormData上传(使用axios) - 图片预览功能(通过转换为Base64) 由于引用中提到了accept属性(引用[2]),我们也可以添加accept属性来限制用户只能选择特定类型的文件,例如图片:accept="image/*"。 我们使用Vue3的<script setup>语法。 注意:在实际项目中,我们可能需要处理文件大小限制、文件类型校验等。 下面是一个示例组件:</think>以下是 Vue3 文件上传的完整实现方案,包含文件选择、预览、上传和错误处理: ### 基础文件上传组件 ```vue <template> <div> <!-- 文件选择区域 --> <input type="file" ref="fileInput" @change="handleFileChange" accept="image/*, .pdf" <!-- 限制文件类型 --> multiple <!-- 允许多选 --> class="hidden" /> <button @click="triggerFileInput">选择文件</button> <!-- 文件预览区域 --> <div v-for="(file, index) in files" :key="index" class="preview-item"> <img v-if="file.type.includes('image')" :src="file.previewUrl" class="preview-image"/> <div v-else class="file-icon">📄</div> <div class="file-info"> <span>{{ file.name }}</span> <span>{{ formatFileSize(file.size) }}</span> </div> <button @click="removeFile(index)">删除</button> </div> <!-- 上传按钮 --> <button @click="uploadFiles" :disabled="isUploading" class="upload-btn" > {{ isUploading ? '上传中...' : '开始上传' }} </button> <!-- 状态提示 --> <div v-if="uploadProgress > 0" class="progress-bar"> <div :style="{ width: uploadProgress + '%' }"></div> </div> <p v-if="errorMessage" class="error">{{ errorMessage }}</p> <p v-if="uploadSuccess" class="success">上传成功!</p> </div> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; const fileInput = ref(null); const files = ref([]); const isUploading = ref(false); const uploadProgress = ref(0); const errorMessage = ref(''); const uploadSuccess = ref(false); // 触发文件选择对话框 const triggerFileInput = () => { fileInput.value.click(); }; // 处理文件选择 const handleFileChange = (e) => { const selectedFiles = Array.from(e.target.files); // 验证文件类型和大小 const validFiles = selectedFiles.filter(file => { if (file.size > 10 * 1024 * 1024) { errorMessage.value = `文件 ${file.name} 超过10MB限制`; return false; } return true; }); // 生成预览URL validFiles.forEach(file => { if (file.type.startsWith('image/')) { file.previewUrl = URL.createObjectURL(file); } files.value.push(file); }); // 重置input以允许重复选择相同文件 e.target.value = null; }; // 删除文件 const removeFile = (index) => { if (files.value[index].previewUrl) { URL.revokeObjectURL(files.value[index].previewUrl); } files.value.splice(index, 1); }; // 格式化文件大小 const formatFileSize = (bytes) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; // 上传文件 const uploadFiles = async () => { if (files.value.length === 0) { errorMessage.value = '请先选择文件'; return; } isUploading.value = true; errorMessage.value = ''; uploadSuccess.value = false; try { const formData = new FormData(); files.value.forEach(file => { formData.append('files', file); }); // 添加额外参数 formData.append('userId', '123'); const response = await axios.post('https://api.example.com/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: progressEvent => { uploadProgress.value = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); } }); if (response.status === 200) { uploadSuccess.value = true; files.value = []; // 清空已上传文件 } } catch (error) { errorMessage.value = `上传失败: ${error.response?.data?.message || error.message}`; } finally { isUploading.value = false; setTimeout(() => { uploadProgress.value = 0; }, 1500); } }; </script> <style scoped> .preview-item { display: flex; align-items: center; padding: 10px; border: 1px solid #eee; margin: 10px 0; } .preview-image { max-width: 100px; max-height: 100px; margin-right: 15px; } .file-info { flex-grow: 1; } .progress-bar { height: 10px; background: #f0f0f0; margin: 15px 0; } .progress-bar > div { height: 100%; background: #42b983; transition: width 0.3s; } .error { color: #ff4757; } .success { color: #42b983; } .hidden { display: none; } .upload-btn:disabled { background: #ccc; cursor: not-allowed; } </style> ``` ### 关键功能说明 1. **文件选择与验证**: - 使用 `accept="image/*, .pdf"` 限制文件类型[^2] - 通过 `multiple` 属性支持多选[^4] - 文件大小验证(限制10MB) 2. **预览功能**: - 图片文件使用 `URL.createObjectURL()` 生成预览[^3] - 非图片文件显示文档图标 3. **上传处理**: - 使用 `FormData` 构造上传数据[^1] - 通过 Axios 发送 POST 请求 - 上传进度条显示(`onUploadProgress`回调) 4. **错误处理**: - 文件类型/大小错误提示 - 网络请求错误捕获 - 上传状态反馈 5. **用户体验优化**: - 自定义按钮触发文件选择 - 文件删除功能 - 上传状态禁用按钮 - 响应式进度反馈 ### 服务端配合示例(Node.js/Express) ```javascript // 文件上传服务端处理 const express = require('express'); const multer = require('multer'); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.array('files'), (req, res) => { const files = req.files; // 处理文件逻辑... res.status(200).json({ message: `${files.length} 个文件上传成功`, paths: files.map(f => f.path) }); }); ``` ### 注意事项 1. 大文件上传需考虑分片上传 2. 敏感文件应在前端和后端双重验证 3. 生产环境需添加CSRF保护 4. 使用`URL.revokeObjectURL()`释放内存 5. 添加文件类型白名单更安全
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值