前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端文件上传功能实现原理

前端文件上传功能实现原理

作者头像
用户6256742
发布2024-05-18 09:29:45
1000
发布2024-05-18 09:29:45
举报
文章被收录于专栏:网络日志网络日志

背景:

最近在做一个上传文件的功能,用的elementUI框架的el-upload组件,为了探究其原理,就想到了有两种上传方式,第一种是type为file的input选择上传,另一个就是拖拽的上传方式,拖拽一直没弄清原理,借这次机会彻底搞清楚。

HTML5实现拖放功能

有两个核心元素拖拽元素放置目标元素,通过这两个元素的触发事件来实现拖放功能。

(1)拖拽元素要赋予draggable属性,属性值为true (2)放置目标元素要在dragover和dragenter事件值中阻止默认行为 (3)放置目标元素在drop事件里可通过DataTransfer对象获取拖拽元素信息

拖拽元素和目标元素的属性和事件:

  • DataTransfer 对象:拖拽对象用来传递的媒介,使用一般为Event.DataTransfer。
  • draggable 属性:就是标签元素要设置draggable=true
  • ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
  • ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
  • ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
  • ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
  • ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上
  • Event.preventDefault()方法:阻止默认的些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。 Event.effectAllowed 属性:就是拖拽的效果。

放置目标事件顺序:

(1) dragenter (2) dragover (3) dragleave 或 drop 只要有元素被拖动到放置目标上,就会触发 dragenter 事件(类似于 mouseover 事件)。紧随其后的是 dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。如果元素被拖出了放置目标,dragover 事件不再发生,但会触发 dragleave 事件(类似于 mouseout事件)。如果元素被放到了放置目标中,则会触发 drop 事件而不是 dragleave 事件。

项目场景图:

前端文件上传功能实现原理
前端文件上传功能实现原理

框架组件实现源码:

代码语言:javascript
复制
//html
<el-upload 
    multiple
    ref="upload"
    :limit="7"
    accept=".json"
    :action="uploadMapData.importUrl"
    :disabled="uploadMapData.isUploading"
    :on-progress="handleFileUploadProgress"
    :on-success="handleFileSuccess"
    :auto-upload="false"
    drag>
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">将json数据拖拽至此处或<em>点击上传</em></em></div>
  </el-upload>
  <div slot="footer" class="dialog-footer">
    <el-button type="primary" @click="submitFileForm">确定</el-button></el-button>
    <el-button @click="uploadMapData.open = false">取消</el-button>
  </div>
//js

非框架组件方式实现

代码语言:javascript
复制
<template>
  <div id="app">
      <div class="content">
        <div class="drag-area" @dragover="fileDragover" @drop="fileDrop">
          <div v-if="fileName" class="file-name">{{ fileName }}</div>
          <div v-else class="uploader-tips">
            <span>将文件拖拽至此,或</span>
            <label for="fileInput" style="color: #11A8FF; cursor: pointer">点此上传</label>
          </div>
        </div>
      </div>
      <div class="footer">
        <input type="file" id="fileInput" @change="chooseUploadFile" style="display: none;">
        <label for="fileInput" v-if="fileName" style="color: #11A8FF; cursor: pointer">选择文件</label>
        <button @click="submit">提交</button>
      </div>
    </div>
</template>

<script>
export default {
  name: 'app',
  data(){
	  return{
		  fileName:'',
		  batchFile:'',
		  maxFileSize:10*1000*1000
	  }
  },
  methods:{
	  // 上传方式获取文件
	  chooseUploadFile(e){
		  console.log(e.target.value)
		  const file = e.target.files.item(0)
		  if(!file)return
		  if(file.size > this.maxFileSize){
			  return alert('文件超过大小')
		  }
		  this.batchFile = file
		  this.fileName = file.name
		  
		  //上传后记得要清空,防止修改文件后再次上传没有反应,这是input的file类型bug
		  e.target.value = ''
	  },
	  // 阻止放置目标元素的dragover的默认行文
	  fileDragover(e){
		  e.preventDefault()
	  },
	  // 拖拽方式获取文件
	  fileDrop(e){
		  e.preventDefault()
		  const file = e.DataTransfer.files[0] //获取第一个上传的文件对象
		  if(!file)return
		  if(file.size > this.maxFileSize){
		  			  return alert('文件超过大小')
		  }
		  this.batchFile = file
		  this.fileName = file.name
	  },
	  async submit(){
		 if(!this.batchFile){
			 return alert('请选择要上传的文件')
		 }
		 let formData = new FormData()
		 formData.append('file',this.batchFile) //FormData 接口的 append() 方法 会添加一个新值到 FormData 对象内的一个已存在的键中,如果键不存在则会添加该键
		 //ajax...
		 let res = await this.$app.uploadToQiniu(formData)
	  }
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
    * {
      font-size: 14px;
    }
    .drag-area {
      height: 200px;
      width: 300px;
      border: dashed 1px gray;
      margin-bottom: 10px;
      color: #777;
    }
    .uploader-tips {
      text-align: center;
      height: 200px;
      line-height: 200px;
    }
    .file-name {
      text-align: center;
      height: 200px;
      line-height: 200px;
    }
</style>

.item(index)介绍

在 HTML 文件上传中,<input type="file"> 元素允许用户选择一个或多个文件进行上传。当用户选择文件后,浏览器会将文件信息存储在 FileList 对象中,该对象是一个类似数组的对象,表示用户选择的文件列表。 FileList 对象有一个 item(index) 方法,该方法用于获取指定索引位置的文件。文件列表是从 0 开始的,所以 .item(0) 表示获取文件列表中的第一个文件。 因此,当你使用 e.target.files.item(0) 时,你实际上是在获取用户选择的文件列表中的第一个文件。如果用户选择了多个文件,你可以使用 .item(1) 获取第二个文件,以此类推。

小思考:为啥不能在input标签上用vue的v-model方式获取文件? input type="file"的input标签不能通过v-model获取文件,只能通过change方法获取

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景:
  • HTML5实现拖放功能
    • 放置目标事件顺序:
    • 项目场景图:
    • 框架组件实现源码:
    • 非框架组件方式实现
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档