Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传

news2024/11/28 18:35:03

目录

 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.files
  console.log('上传的文件:', files)
}

现在就可以实现文件的拖拽上传了,只要拖拽到指定的区域就可以,拿到数据使用的是e.dataTransfer.flies

2.处理文件的上传

处理数据

我们可以定义一个数组放至待上传的文件,一个放置已经进行处理过的文件

//存放已经上传的文件的数组
let fileListOver = ref([])
//存放要上传的文件的数组
let fileList = ref([])

在拖拽事件后数据push进数组中

// 处理拖拽事件
const handleDrop = (e) => {
  e.preventDefault()
  const files = e.dataTransfer.files
  console.log('上传的文件:', files)
  //将要上传的文件放入数组中
  fileList.value.push(...files)
  uploadFile()
}

上传文件的函数

这个上传文件的接口使用的是上一个视频写的接口,没看过的可以翻翻前面的Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)_専心的博客-CSDN博客

接下来就可以进行文件的上传,处理上传的数据,因为我们是可以选择多文件的,所以要递归判断上传文件。在这之前别忘了导入axios的构造函数

 

//上传文件的函数
const uploadFile = async () => {
  //先要计算出要上传的文件的索引
  const index = fileListOver.value.length
  if (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.files
  console.log('上传的文件:', files)
  //将要上传的文件放入数组中
  fileList.value.push(...files)
  dragStyle.value = false
  uploadFile()
}
//上传文件的函数
const uploadFile = async () => {
  //先要计算出要上传的文件的索引
  const index = fileListOver.value.length
  if (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或者某个容器为拖拽对象,这种拖拽上传的问题就迎刃而解了,如有不理解或更好的方案可以私信或评论交流。

下一篇准备实现大文件的分片上传,欢迎关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1096378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JVM】JVM的内存区域划分

JVM的内存区域划分 堆Java虚拟机栈程序计数器方法区运行时常量池 堆 程序中创建的所有对象都保存在堆中 Java虚拟机栈 Java虚拟机栈的生命周期和线程相同,描述的是Java方法执行的内存模型,每个方法在执行的时候都会同时创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法…

C语言 —— 结构体

生活中有许多复杂对象是无法用基本数据类型来描述的, 于是为了描述复杂对象, C语言就会使用到结构体. 1. 结构体的声明与定义 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明与定义 struct tag {…

vue3后台管理框架之基础配置

配置vite.config.js import { defineConfig } from viteimport vue from @vitejs/plugin-vueexport default defineConfig(({ command, mode }) => {//const env = loadEnv(mode, process.cwd(), ) //获取环境变量return {// 打包devbase: ./,// 开发环境server: {port: 50…

Spring framework Day11:策略模式中注入所有实现类

前言 什么是策略模式&#xff1f; 策略模式&#xff08;Strategy Pattern&#xff09;是一种面向对象设计模式&#xff0c;它定义了算法族&#xff08;一组相似的算法&#xff09;&#xff0c;并且将每个算法都封装起来&#xff0c;使得它们可以互相替换。策略模式让算法的变…

LeetCode【17】电话号码的字母组合

题目&#xff1a; 思路&#xff1a; 参考&#xff1a;https://blog.csdn.net/weixin_46429290/article/details/121888154 和上一个题《子集》的思路一样&#xff0c;先画出树结构&#xff0c;看树的深度&#xff08;遍历层级&#xff09;&#xff0c;树的宽度&#xff08;横向…

压力山大题

找不到工作的面试者总结 提示&#xff1a;写文章的时候&#xff0c;我还在找工作&#xff01;&#xff01;&#xff01; 文章目录 找不到工作的面试者总结前言一、JAVA面死题1. OOP是什么2. 重载与重写的区别3. java基本类型4. String、StringBuffer、StringBuilder的区别 二、…

2022最新版-李宏毅机器学习深度学习课程-P23 为什么用了验证集结果还是过拟合

用了验证集还有可能会过拟合 这个片段可以从理论上证明这一点 以上整个挑选模型的过程也可以想象为一种训练。 把三个模型导出的最小损失公式看成一个集合&#xff0c;现在要做的就是在这个集合中找到某个h&#xff08;此处可以视为训练&#xff09;&#xff0c;使得在验证集…

边写代码边学习之Pycaret

PyCaret 简介 PyCaret 是一个用于简化 Python 机器学习工作流程的开源库。它提供了一个高级、低代码的接口&#xff0c;用于自动化机器学习流程的各个方面&#xff0c;使数据科学家和分析师更容易构建和部署机器学习模型。PyCaret 的一些关键特点和用途包括&#xff1a; 1. 自…

第六章 查找

第六章 查找 基本概念静态查找表顺序表上的查找有序表上的查找索引顺序表上的查找 二叉排序树散列表常见散列法散列表的实现 小试牛刀 基本概念 查找表是由同一类型的数据元素构成的集合&#xff0c;它是一种以查找为“核心”&#xff0c;同时包括其他运算的非常灵活的数据结构…

Android Studio SDK manager加载packages不全

打开Android Studio里的SDK manager&#xff0c;发现除了已安装的&#xff0c;其他的都不显示。 解决方法&#xff1a; 设置代理&#xff1a; 方便复制> http://mirrors.neusoft.edu.cn/ 重启Android Studio

小主机折腾记17

8月9月10月基本在出差&#xff0c;流水账如下 1.由于出差&#xff0c;租了个公寓&#xff0c;所以买了个r2s&#xff0c;卖家已经安装部署好openwrt&#xff0c;风扇以及无线网卡 着重研究了风扇的脚本以及无线网卡的设置 风扇可以完美设置&#xff0c;但是无线网卡效果差强人意…

k8s-11 网络策略

添加网络策略 限制pod流量 控制的对象是具有appmyapp-v1标签的pod 此时访问svc是不通的 给测试pod添加指定标签后&#xff0c;可以访问 重启一下 限制namespace流量 给namespace添加指定标签 同时限制namespace和pod 给test命令空间中的pod添加指定标签后才能访问 限制集群…

k8s-13 存储之secret

Secret 对象类型用来保存敏感信息&#xff0c;例如密码、OAuth 令牌和 ssh key。 敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活 。 Pod 可以用两种方式使用 secret:作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里 当 kubelet 为 pod 拉…

山西电力市场日前价格预测【2023-10-16】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-16&#xff09;山西电力市场全天平均日前电价为356.38元/MWh。其中&#xff0c;最高日前电价为502.82元/MWh&#xff0c;预计出现在18: 30。最低日前电价为224.63元/MWh&#xff0c;预计…

PTQ与QAT

对称量化与非对称量化 量化分为对称量化与非对称量化。 非对称量化含有S和Z&#xff0c;对称量化Z为0&#xff0c;计算公式中只需S&#xff0c;为非饱和量化。 动态范围的确认 动态范围的确认Max&#xff08;默认的是对称量化&#xff0c;即不用Z&#xff09;&#xff0c;…

linux 内核中的pid和前缀树

前言&#xff1a; 写这个文章的初衷是因为今天手写了一个字典树&#xff0c;然后写字典树以后忽然想到了之前看的技术文章&#xff0c;linux kernel 之前的pid 申请方式已经从 bitmap 变成了 基数树&#xff0c;所以打算写文章再回顾一下这种数据结构算法 一、内核中pid的申请…

排序算法-基数排序法(RadixSort)

排序算法-基数排序法&#xff08;RadixSort&#xff09; 1、说明 基数排序法与我们之前讨论的排序法不太一样&#xff0c;并不需要进行元素之间的比较操作&#xff0c;而是属于一种分配模式排序方式。 基数排序法比较的方向可分为最高位优先&#xff08;Most Significant Di…

初识容器Docker

目前使用 Docker 基本上有两个选择&#xff1a;Docker Desktop和Docker Engine。Docker Desktop 是专门针对个人使用而设计的&#xff0c;支持 Mac 和 Windows 快速安装&#xff0c;具有直观的图形界面&#xff0c;还集成了许多周边工具&#xff0c;方便易用。 不是太推荐使用D…

【linux kernel】linux内核设备驱动的注册机制

文章目录 1、简介2、driver_register分析&#x1f449;&#xff08;2-1&#xff09;driver_find分析&#x1f449;&#xff08;2-2&#xff09;bus_add_driver分析 3、总结 &#x1f53a;【linux内核系列文章】 &#x1f449;对一些文章内容进行了勘误&#xff0c;本系列文章长…