Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传
目录
1.拖拽上传的剖析
input的file默认拖动
让其他的盒子成为拖拽对象
2.处理文件的上传
处理数据
上传文件的函数
兼顾点击事件
渲染已处理过的文件
测试效果
3.总结
博客主页:専心_前端,javascript,mysql-CSDN博客
系列专栏:vue3+nodejs 实战--文件上传
前端代码仓库:jiangjunjie666/my-upload: vue3+nodejs 上传文件的项目,用于学习 (github.com)
后端代码仓库:jiangjunjie666/my-upload-server: nodejs上传文件的后端 (github.com)
欢迎关注
在上一篇中,我们实现了文件的批量上传以及显示实时的上传进度,Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)_専心的博客-CSDN博客
该篇主要的是探讨拖拽上传的思路以及实现。
1.拖拽上传的剖析
input的file默认拖动
其实在我的目前的认知中(当然我不是很成熟,还在努力学习,如有不对请大佬们多包含),上传文件其实主要就是二点,第一个是页面的交互(拖拽的样式,显示的上传进度条等等),第二个就是网络通信将文件通过post请求发送给后端,所以上传文件的接口我们上期中已经写好了,这次主要是完成前端页面的交互逻辑。
拖拽上传其实在原生的input选择框中本来就是支持的。
所以我们可以将input铺满整个上传框中,并且将其opacity设置为0,就能达到隐藏的效果,并且既能支持拖拽还能支持点击。
但是一般情况下实际开发中不会这样做,一般都是根据设计稿来具体实现不同的样式,input框都是隐藏状态,隐藏状态就无法做到拖拽文件到其范围里面了。
让盒子成为拖拽对象
上面的input之所以支持拖拽效果,因为在原生的html5中他就是被作为一个拖拽对象存在的,但是兼容性可能不是特别好,这时候我们就可以将其他的盒子也变成一个拖拽对象。
下面是我们存放整个拖拽上传区域的盒子,我们将其设置为拖拽对象。
<div class="container" @dragenter.prevent="handleDragEnter" @dragover.prevent="handleDragOver" @drop.prevent="handleDrop"></div>
我们在他身上绑定了一些事件
-
dragover
事件:当拖拽对象在一个元素上悬停时(即,拖拽对象在元素上移动时),dragover
事件会持续触发。 -
dragenter
事件:当拖拽对象首次进入一个元素时,dragenter
事件会触发。 -
dragleave
事件:当拖拽对象离开一个元素时,dragleave
事件会触发。 -
drop
事件:当拖拽对象在元素上松开鼠标按钮时,drop
事件会触发。
我们这里只需要将enter和over阻止掉就行
// 处理拖拽进入
const handleDragEnter = (e) => {e.preventDefault()console.log('Drag entered')
}
// 处理拖拽过程中
const handleDragOver = (e) => {e.preventDefault() // 阻止默认行为// 处理拖拽过程中的操作console.log('Drag over')
}
// 处理拖拽事件
const handleDrop = (e) => {e.preventDefault()const files = e.dataTransfer.filesconsole.log('上传的文件:', files)
}
现在就可以实现文件的拖拽上传了,只要拖拽到指定的区域就可以,拿到数据使用的是e.dataTransfer.flies
2.处理文件的上传
处理数据
我们可以定义一个数组放至待上传的文件,一个放置已经进行处理过的文件
//存放已经上传的文件的数组
let fileListOver = ref([])
//存放要上传的文件的数组
let fileList = ref([])
在拖拽事件后数据push进数组中
// 处理拖拽事件
const handleDrop = (e) => {e.preventDefault()const files = e.dataTransfer.filesconsole.log('上传的文件:', files)//将要上传的文件放入数组中fileList.value.push(...files)uploadFile()
}
上传文件的函数
这个上传文件的接口使用的是上一个视频写的接口,没看过的可以翻翻前面的Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)_専心的博客-CSDN博客
接下来就可以进行文件的上传,处理上传的数据,因为我们是可以选择多文件的,所以要递归判断上传文件。在这之前别忘了导入axios的构造函数
//上传文件的函数
const uploadFile = async () => {//先要计算出要上传的文件的索引const index = fileListOver.value.lengthif (fileList.value.length == fileListOver.value.length) {//所有的数据都已经上传完毕,退出递归return}//存放文件数据let formData = new FormData()formData.append('file', fileList.value[index])console.log(formData)let res = await http.post('/api/fileUpload', formData)if (res.code !== 200) {fileListOver.value.push({name: fileList.value[index].name,size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',status: 'error'})ElMessage({type: 'error',message: res.msg})} else {//将上传好的数据插入至fileListOver中fileListOver.value.push({name: fileList.value[index].name,size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',status: 'scuuess'})ElMessage({type: 'success',message: '上传成功'})}//开个定时器let timer = setTimeout(() => {uploadFile() //递归clearTimeout(timer)}, 1000)
}
兼顾点击事件
因为我们拖拽文件肯定也是支持选择文件的,所以这里做一下兼容,其实很简单,看过前几期的写这个就是信手拈来。
let fileInputRef = ref(null)// input的监听事件
const handlerChange = (e) => {//将点击上传的文件添加到fileList中fileList.value.push(...e.target.files)// 调用函数uploadFile()
}// 点击上传按钮
const handlerUpload = () => {fileInputRef.value.click()
}
渲染已处理过的文件
<!-- 这里显示已经拖拽上传了的文件 --><el-table :data="fileListOver" style="width: 80%"><el-table-column prop="name" label="文件名" width="450" /><el-table-column prop="size" label="文件大小" width="200" /><!-- 控制显示 --><el-table-column label="文件状态" width="300"><template #default="scope1"><span v-if="scope1.row.status == 'scuuess'" style="color: #67c23a">上传成功</span><span v-if="scope1.row.status == 'error'" style="color: red">上传失败</span></template></el-table-column></el-table>
测试效果
选中
上传中
上传完成
全部代码
<template><div class="container" :class="{ draging: dragStyle == true }" @dragenter.prevent="handleDragEnter" @dragover.prevent="handleDragOver" @drop.prevent="handleDrop"><el-icon size="200" class="icon" @click="handlerUpload"><UploadFilled /></el-icon><input type="file" multiple @change="handlerChange" ref="fileInputRef" class="ipt" style="display: none" /></div><!-- 这里显示已经拖拽上传了的文件 --><el-table :data="fileListOver" style="width: 80%"><el-table-column prop="name" label="文件名" width="450" /><el-table-column prop="size" label="文件大小" width="200" /><!-- 控制显示 --><el-table-column label="文件状态" width="300"><template #default="scope1"><span v-if="scope1.row.status == 'scuuess'" style="color: #67c23a">上传成功</span><span v-if="scope1.row.status == 'error'" style="color: red">上传失败</span></template></el-table-column></el-table>
</template><script setup>
import { ref } from 'vue'
import { http } from '@/api/http.js'
import { ElMessage } from 'element-plus'
let fileInputRef = ref(null)
//存放已经上传的文件的数组
let fileListOver = ref([])
//存放要上传的文件的数组
let fileList = ref([])
//拖拽样式
let dragStyle = ref(false)
// input的监听事件
const handlerChange = (e) => {//将点击上传的文件添加到fileList中fileList.value.push(...e.target.files)// 调用函数uploadFile()
}// 点击上传按钮
const handlerUpload = () => {fileInputRef.value.click()
}// 处理拖拽进入
const handleDragEnter = (e) => {e.preventDefault()//添加拖拽样式dragStyle.value = true
}
// 处理拖拽过程中
const handleDragOver = (e) => {e.preventDefault() // 阻止默认行为
}
// 处理拖拽事件
const handleDrop = (e) => {e.preventDefault()const files = e.dataTransfer.filesconsole.log('上传的文件:', files)//将要上传的文件放入数组中fileList.value.push(...files)dragStyle.value = falseuploadFile()
}
//上传文件的函数
const uploadFile = async () => {//先要计算出要上传的文件的索引const index = fileListOver.value.lengthif (fileList.value.length == fileListOver.value.length) {//所有的数据都已经上传完毕,退出递归return}//存放文件数据let formData = new FormData()formData.append('file', fileList.value[index])console.log(formData)let res = await http.post('/api/fileUpload', formData)if (res.code !== 200) {fileListOver.value.push({name: fileList.value[index].name,size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',status: 'error'})ElMessage({type: 'error',message: res.msg})} else {//将上传好的数据插入至fileListOver中fileListOver.value.push({name: fileList.value[index].name,size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',status: 'scuuess'})ElMessage({type: 'success',message: '上传成功'})}//开个定时器let timer = setTimeout(() => {uploadFile() //递归clearTimeout(timer)}, 1000)
}
</script><style lang="scss" scoped>
.container {width: 800px;height: 300px;margin: 20px 100px;border: 2px dashed #ccc;display: flex;justify-content: center;align-items: center;.ipt {width: 100%;height: 100%;opacity: 0;display: none;}.icon {color: #ccc;}.icon:hover {cursor: pointer;}//拖拽样式.draging {background-color: #ecf5ff;border: 2px dashed #eaebec;.icon {color: pink;}}
}
</style>
3.总结
拖拽上传的本质就是用户与页面的交互,其实涉及到的难点不多,只要懂得了设置div或者某个容器为拖拽对象,这种拖拽上传的问题就迎刃而解了,如有不理解或更好的方案可以私信或评论交流。
下一篇准备实现大文件的分片上传,欢迎关注。
相关文章:

Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传
目录 1.拖拽上传的剖析 input的file默认拖动 让其他的盒子成为拖拽对象 2.处理文件的上传 处理数据 上传文件的函数 兼顾点击事件 渲染已处理过的文件 测试效果 3.总结 博客主页:専心_前端,javascript,mysql-CSDN博客 系列专栏:vue3nodejs 实战-…...

Windows:VS Code IDE安装ESP-IDF【保姆级】
物联网开发学习笔记——目录索引 参考: VS Code官网:Visual Studio Code - Code Editing. Redefined 乐鑫官网:ESP-IDF 编程指南 - ESP32 VSCode ESP-ID Extension Install 一、前提条件 Visual Studio Code IDE安装ESP-IDF扩展…...

Hadoop3教程(十一):MapReduce的详细工作流程
文章目录 (94)MR工作流程Map阶段Reduce阶段 参考文献 (94)MR工作流程 本小节将展示一下整个MapReduce的全工作流程。 Map阶段 首先是Map阶段: 首先,我们有一个待处理文本文件的集合; 客户端…...

测试中Android与IOS分别关注的点
目录 1、自身不同点 2、测试注重点 3、其他测试点 主要从本身系统的不同点、系统造成的不同点、和注意的测试点做总结 1、自身不同点 研发商:Adroid是google公司做的手机系统,IOS是苹果公司做的手机系统 开源程度:Android是开源的&a…...
NLG(自然语言生成)评估指标介绍
诸神缄默不语-个人CSDN博文目录 本文介绍自然语言生成任务中的各种评估指标。 因为我是之前做文本摘要才接触到这一部分内容的,所以本文也是文本摘要中心。 持续更新。 文章目录 1. 常用术语2. ROUGE (Recall Oriented Understudy for Gisting Evaluation)1. 计算…...

苍穹外卖(七) Spring Task 完成订单状态定时处理
Spring Task 完成订单状态定时处理, 如处理支付超时订单 Spring Task介绍 Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。 应用场景: 信用卡每月还款提醒 火车票售票系统处理未支付订单 入职纪念日为用户发送通知 点外…...

【探索Linux】—— 强大的命令行工具 P.11(基础IO,文件操作)
阅读导航 前言一、C语言的文件操作二、C的文件操作三、Linux系统文件操作(I/O接口)1. open()⭕传入多个打开方式(按位或操作将不同的标志位组合在一起) 2. write()3. read()4. close()5. lseek() 温馨提示 前言 前面我们讲了C语言…...

前端练习项目(附带页面psd图片及react源代码)
一、前言 相信很多学完前端的小伙伴都想找个前端项目练练手,检测自己的学习成果。但是现在很多项目市面上都烂大街了。今天给大家推荐一个全新的项目——电子校园 项目位置:https://github.com/v5201314/eSchool 二、项目介绍(部分页面展示)ÿ…...

【从零开始学习Redis | 第三篇】在Java中操作Redis
前言: 本文算是一期番外,介绍一下如何在Java中使用Reids ,而其实基于Java我们有很多的开源框架可以用来操作redis,而我们今天选择介绍的是其中比较常用的一款:Spring Data Redis 目录 前言: Spring Data…...

vim、gcc/g++、make/Makefile、yum、gdb
vim、gcc/g、make/Makefile、yum、gdb 一、Linux编辑器vim1、简介2、三种模式的概念(1)正常/普通/命令模式(Normal mode)(2)插入模式(Insert mode)(3)末行/底行模式(last line mode) 3、三种模式的切换4、正…...

2022最新版-李宏毅机器学习深度学习课程-P13 局部最小值与鞍点
一、优化失败的原因 局部最小值?鞍点? 二、数学推导分析 用泰勒公式展开 一项与梯度(L的一阶导)有关,一项与海赛矩阵(L的二阶导)有关 海瑟矩阵 VTHV通过海瑟矩阵的性质可以转为判断H是否是正…...

ARM架构的基本知识
ARM两种授权 体系结构授权, 一种硬件规范, 用来约定指令集, 芯片内部体系结构(内存管理, 高速缓存管理), 只约定每一条指令的格式, 行为规范, 参数, 客户根据这个规范自行设计与之兼容的处理器处理IP授权, ARM公司根据某个版本的体系结构设计处理器, 再把处理器设计方案授权给…...

网络安全(黑客技术)——如何高效自学
前言 前几天发布了一篇 网络安全(黑客)自学 没想到收到了许多人的私信想要学习网安黑客技术!却不知道从哪里开始学起!怎么学?如何学? 今天给大家分享一下,很多人上来就说想学习黑客,…...

云原生场景下高可用架构的最佳实践
作者:刘佳旭(花名:佳旭),阿里云容器服务技术专家 引言 随着云原生技术的快速发展以及在企业 IT 领域的深入应用,云原生场景下的高可用架构,对于企业服务的可用性、稳定性、安全性越发重要。通…...
图论-最短路径算法-弗洛伊德算法与迪杰斯特拉算法
弗洛伊德算法: 弗洛伊德算法本质是动态规划,通过添加点进如可选择的点组成的集合的同时更新所有点之间的距离,从而得到每两个点之间的最短距离。 初始化: 创建一个二维数组 dist,其中 dist[i][j] 表示从节点 i 到节点…...

[23] IPDreamer: Appearance-Controllable 3D Object Generation with Image Prompts
pdf Text-to-3D任务中,对3D模型外观的控制不强,本文提出IPDreamer来解决该问题。在NeRF Training阶段,IPDreamer根据文本用ControlNet生成参考图,并将参考图作为Zero 1-to-3的控制条件,用基于Zero 1-to-3的SDS损失生成…...

深入理解React中的useEffect钩子函数
引言: React是一种流行的JavaScript库,它通过组件化和声明式编程的方式简化了前端开发。在React中,一个核心概念是组件的生命周期,其中包含了许多钩子函数,用于管理组件的不同阶段。其中之一就是useEffect钩子函数&…...

数字化时代的财务管理:挑战与机遇
导语:随着数字化技术的不断发展,财务管理正面临着前所未有的挑战和机遇。数字化不仅改变了财务数据的收集、处理和分析方式,还为财务决策提供了更多的依据和方向。本文将探讨数字化时代财务管理的新特点,以及如何利用数字化技术提…...

网络通信协议-HTTP、WebSocket、MQTT的比较与应用
在今天的数字化世界中,各种通信协议起着关键的作用,以确保信息的传递和交换。HTTP、WebSocket 和 MQTT 是三种常用的网络通信协议,它们各自适用于不同的应用场景。本文将比较这三种协议,并探讨它们的主要应用领域。 HTTPÿ…...
【深度学习】深度学习实验四——循环神经网络(RNN)、dataloader、长短期记忆网络(LSTM)、门控循环单元(GRU)、超参数对比
一、实验内容 实验内容包含要进行什么实验,实验的目的是什么,实验用到的算法及其原理的简单介绍。 1.1 循环神经网络 (1)理解序列数据处理方法,补全面向对象编程中的缺失代码,并使用torch自带数据工具将数据封装为dataloader。 (2)分别采用手动方式以及调用接口方式…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...