目录
博客主页:専心_前端,javascript,mysql-CSDN博客
系列专栏:vue3+nodejs 实战--文件上传
前端代码仓库:jiangjunjie666/my-upload: vue3+nodejs 上传文件的项目,用于学习 (github.com)
后端代码仓库:jiangjunjie666/my-upload-server: nodejs上传文件的后端 (github.com)
欢迎关注
在上一篇中,我们实现了文件的批量上传以及显示实时的上传进度,Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)_専心的博客-CSDN博客
该篇主要的是探讨拖拽上传的思路以及实现。
其实在我的目前的认知中(当然我不是很成熟,还在努力学习,如有不对请大佬们多包含),上传文件其实主要就是二点,第一个是页面的交互(拖拽的样式,显示的上传进度条等等),第二个就是网络通信将文件通过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
我们可以定义一个数组放至待上传的文件,一个放置已经进行处理过的文件
- //存放已经上传的文件的数组
- 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>
选中

上传中
上传完成
- <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>
-
- <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>
拖拽上传的本质就是用户与页面的交互,其实涉及到的难点不多,只要懂得了设置div或者某个容器为拖拽对象,这种拖拽上传的问题就迎刃而解了,如有不理解或更好的方案可以私信或评论交流。
下一篇准备实现大文件的分片上传,欢迎关注。