• 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阻止掉就行

    1. // 处理拖拽进入
    2. const handleDragEnter = (e) => {
    3. e.preventDefault()
    4. console.log('Drag entered')
    5. }
    6. // 处理拖拽过程中
    7. const handleDragOver = (e) => {
    8. e.preventDefault() // 阻止默认行为
    9. // 处理拖拽过程中的操作
    10. console.log('Drag over')
    11. }
    12. // 处理拖拽事件
    13. const handleDrop = (e) => {
    14. e.preventDefault()
    15. const files = e.dataTransfer.files
    16. console.log('上传的文件:', files)
    17. }

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

    2.处理文件的上传

    处理数据

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

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

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

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

    上传文件的函数

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

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

     

    1. //上传文件的函数
    2. const uploadFile = async () => {
    3. //先要计算出要上传的文件的索引
    4. const index = fileListOver.value.length
    5. if (fileList.value.length == fileListOver.value.length) {
    6. //所有的数据都已经上传完毕,退出递归
    7. return
    8. }
    9. //存放文件数据
    10. let formData = new FormData()
    11. formData.append('file', fileList.value[index])
    12. console.log(formData)
    13. let res = await http.post('/api/fileUpload', formData)
    14. if (res.code !== 200) {
    15. fileListOver.value.push({
    16. name: fileList.value[index].name,
    17. size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',
    18. status: 'error'
    19. })
    20. ElMessage({
    21. type: 'error',
    22. message: res.msg
    23. })
    24. } else {
    25. //将上传好的数据插入至fileListOver中
    26. fileListOver.value.push({
    27. name: fileList.value[index].name,
    28. size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',
    29. status: 'scuuess'
    30. })
    31. ElMessage({
    32. type: 'success',
    33. message: '上传成功'
    34. })
    35. }
    36. //开个定时器
    37. let timer = setTimeout(() => {
    38. uploadFile() //递归
    39. clearTimeout(timer)
    40. }, 1000)
    41. }

    兼顾点击事件

    因为我们拖拽文件肯定也是支持选择文件的,所以这里做一下兼容,其实很简单,看过前几期的写这个就是信手拈来。

    1. let fileInputRef = ref(null)
    2. // input的监听事件
    3. const handlerChange = (e) => {
    4. //将点击上传的文件添加到fileList中
    5. fileList.value.push(...e.target.files)
    6. // 调用函数
    7. uploadFile()
    8. }
    9. // 点击上传按钮
    10. const handlerUpload = () => {
    11. fileInputRef.value.click()
    12. }

    渲染已处理过的文件

    1. <el-table :data="fileListOver" style="width: 80%">
    2. <el-table-column prop="name" label="文件名" width="450" />
    3. <el-table-column prop="size" label="文件大小" width="200" />
    4. <el-table-column label="文件状态" width="300">
    5. <template #default="scope1">
    6. <span v-if="scope1.row.status == 'scuuess'" style="color: #67c23a">上传成功span>
    7. <span v-if="scope1.row.status == 'error'" style="color: red">上传失败span>
    8. template>
    9. el-table-column>
    10. el-table>

    测试效果

    选中

    上传中

     

    上传完成 

     

    全部代码

    1. <script setup>
    2. import { ref } from 'vue'
    3. import { http } from '@/api/http.js'
    4. import { ElMessage } from 'element-plus'
    5. let fileInputRef = ref(null)
    6. //存放已经上传的文件的数组
    7. let fileListOver = ref([])
    8. //存放要上传的文件的数组
    9. let fileList = ref([])
    10. //拖拽样式
    11. let dragStyle = ref(false)
    12. // input的监听事件
    13. const handlerChange = (e) => {
    14. //将点击上传的文件添加到fileList中
    15. fileList.value.push(...e.target.files)
    16. // 调用函数
    17. uploadFile()
    18. }
    19. // 点击上传按钮
    20. const handlerUpload = () => {
    21. fileInputRef.value.click()
    22. }
    23. // 处理拖拽进入
    24. const handleDragEnter = (e) => {
    25. e.preventDefault()
    26. //添加拖拽样式
    27. dragStyle.value = true
    28. }
    29. // 处理拖拽过程中
    30. const handleDragOver = (e) => {
    31. e.preventDefault() // 阻止默认行为
    32. }
    33. // 处理拖拽事件
    34. const handleDrop = (e) => {
    35. e.preventDefault()
    36. const files = e.dataTransfer.files
    37. console.log('上传的文件:', files)
    38. //将要上传的文件放入数组中
    39. fileList.value.push(...files)
    40. dragStyle.value = false
    41. uploadFile()
    42. }
    43. //上传文件的函数
    44. const uploadFile = async () => {
    45. //先要计算出要上传的文件的索引
    46. const index = fileListOver.value.length
    47. if (fileList.value.length == fileListOver.value.length) {
    48. //所有的数据都已经上传完毕,退出递归
    49. return
    50. }
    51. //存放文件数据
    52. let formData = new FormData()
    53. formData.append('file', fileList.value[index])
    54. console.log(formData)
    55. let res = await http.post('/api/fileUpload', formData)
    56. if (res.code !== 200) {
    57. fileListOver.value.push({
    58. name: fileList.value[index].name,
    59. size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',
    60. status: 'error'
    61. })
    62. ElMessage({
    63. type: 'error',
    64. message: res.msg
    65. })
    66. } else {
    67. //将上传好的数据插入至fileListOver中
    68. fileListOver.value.push({
    69. name: fileList.value[index].name,
    70. size: fileList.value[index].size > 1024 * 1024 ? (fileList.value[index].size / 1024 / 1024).toFixed(2) + 'mb' : (fileList.value[index].size / 1024).toFixed(2) + 'kb',
    71. status: 'scuuess'
    72. })
    73. ElMessage({
    74. type: 'success',
    75. message: '上传成功'
    76. })
    77. }
    78. //开个定时器
    79. let timer = setTimeout(() => {
    80. uploadFile() //递归
    81. clearTimeout(timer)
    82. }, 1000)
    83. }
    84. script>
    85. <style lang="scss" scoped>
    86. .container {
    87. width: 800px;
    88. height: 300px;
    89. margin: 20px 100px;
    90. border: 2px dashed #ccc;
    91. display: flex;
    92. justify-content: center;
    93. align-items: center;
    94. .ipt {
    95. width: 100%;
    96. height: 100%;
    97. opacity: 0;
    98. display: none;
    99. }
    100. .icon {
    101. color: #ccc;
    102. }
    103. .icon:hover {
    104. cursor: pointer;
    105. }
    106. //拖拽样式
    107. .draging {
    108. background-color: #ecf5ff;
    109. border: 2px dashed #eaebec;
    110. .icon {
    111. color: pink;
    112. }
    113. }
    114. }
    115. style>

    3.总结

     拖拽上传的本质就是用户与页面的交互,其实涉及到的难点不多,只要懂得了设置div或者某个容器为拖拽对象,这种拖拽上传的问题就迎刃而解了,如有不理解或更好的方案可以私信或评论交流。

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

  • 相关阅读:
    以TrueType为例谈字形描述
    探索微控制器世界:MicroPython的奇妙之旅
    mongodb跨数据中心备份
    Web安全总结
    【Unity3D】刚体组件Rigidbody
    【2022版】Spring面试题整理(含答案解析)
    超低延时直播技术演进之路-进化篇
    刷题记录:牛客NC51170石子合并
    CameraServiceProxy启动-Android12
    springboot启动项问题,服务无法被访问
  • 原文地址:https://blog.csdn.net/m0_64642443/article/details/133828584