最近应用了elementUI提供的upload组件上传文件,总结下应用过程中的一些心得。本文不会介绍如何应用elementUI中的upload组件,因为官网自身就曾经介绍的很具体了。
本文次要包含以下几个问题:
- 文件校验是如何做的,是否同时校验多种文件格式;
- 上传文件的时候是否自定义拖拽款式;
- 点击的时候如何实现手动上传。
序言
- 本文波及到源码的局部,以v2.15.1为例;
- upload组件源代码在
/packages/upload
文件夹上面;
文件校验是如何做的,是否同时校验多种文件格式
upload组件提供了两种上传文件的形式:
- input元素形式:通过input元素触发零碎的文件抉择窗口来抉择文件;
- 拖拽形式:通过原生drag和drop API来抉择文件。
针对上述两种形式,upload组件提供了属性accept
来限度和校验文件格式。这里,我应用了两个词:
- 限度:从源头层面上限度,也就是不是特定的格局就抉择不了,实用于input元素形式;
校验:从后果层面上校验,也就是在最初一步能力发现抉择不了,同时实用于上述两种形式,其中校验又包含两种:
- 源码自身蕴含的校验逻辑;
- 自定义校验。
接下来咱们联合upload组件源码来剖析下实现原理。
限度
限度模式的次要原理就是原生input元素的accept属性(源码/packages/upload/src/upload.vue文件第206行):
<input class="el-upload__input" type="file" ref="input" name={name} on-change={handleChange} multiple={multiple} accept={accept}></input>
当没有增加accept属性的时候,零碎的文件抉择框中的文件都是可选的:
<input type="file" />
当增加了accept之后,比方.png
,零碎的文件抉择框中只有合乎格局的文件才是可选的:
<input type="file" accept=".png" />
如何反对多种文件格式上传呢?看下原生input元素accept属性的文档,会发现accept是反对逗号宰割的多种文件格式的:
<input type="file" accept=".png,.jpg,.jpeg" />
开发的时候发现一个有意思的问题,就是如果accept外面.jpg和.jpeg只写一个,另外一种格局也是能够抉择的:
<input type="file" accept=".png,.jpeg" />// 或者<input type="file" accept=".png,.jpg" />
所以,jpg和jpeg有啥关系?查找了下材料,发现这两种格局并没有任何区别,至于为什么会有两个名字,说是先有了jpeg,而后Windows的晚期版本规定文件的扩展名只能是三个字符,所以就变成了jpg。感兴趣的能够参考这篇文章。
所以,
- 只通过accept的模式限度文件类型有时候可能不精确;
- Windows零碎可能会有兼容性问题,也就是即便提供了accept属性,其余格局的文件也是可选的;
这个时候就只能依赖上面的校验形式了。
源码自身蕴含的校验逻辑
当应用拖拽形式上传文件的时候,是能够拖拽任何文件的,所以只能在拖拽实现的时候去校验文件格局。那么,应该去校验哪些格局呢?从upload组件使用者的角度来说,无论是input元素形式还是拖拽形式,都应该是放弃对立的。也就是,原生input的accept属性反对的格局,在应用拖拽形式的时候,也都得反对。
所以,先看下input的accept属性反对的格局类型:
- 文件扩展名模式,以
.
结尾; - MIME类型;
- "audio/*" "video/*" "image/*"。
upload组件源码针对上述三种类型做的相应解决(/packages/upload/src/upload-dragger.vue
文件onDrop办法)如下:
this.$emit('file', [].slice.call(e.dataTransfer.files).filter(file => { const { type, name } = file; const extension = name.indexOf('.') > -1 ? `.${ name.split('.').pop() }` : ''; const baseType = type.replace(/\/.*$/, ''); return accept.split(',') .map(type => type.trim()) .filter(type => type) .some(acceptedType => { if (/\..+$/.test(acceptedType)) { // 1. 扩展名模式 return extension === acceptedType; } if (/\/\*$/.test(acceptedType)) { // 3. "audio/*" "video/*" "image/*" return baseType === acceptedType.replace(/\/\*$/, ''); } if (/^[^\/]+\/[^\/]+$/.test(acceptedType)) { // 2. MIME类型 return type === acceptedType; } return false; });}));
自定义校验
upload组件提供了自定义校验的钩子函数:before-upload。依据该函数的返回值决定是持续还是终止(/packages/upload/src/upload.vue
文件第91行):
const before = this.beforeUpload(rawFile);
上传文件的时候是否自定义拖拽款式
论断:不能
尝试
dragEvent有一个dataTransfer属性,这个属性有一个setDragImage办法,该办法看似能够用来自定义拖拽款式。然而,这个办法有一个应用限度:只能在dragstart事件中调用。
然而当从操作系统拖拽文件到浏览器中的时候,dragstart事件是不会触发的,所以setDragImage办法不能实现咱们的目标。
点击的时候如何实现手动上传
upload组件dom中蕴含一个不显示的input元素,当点击组件的时候触发input元素的click事件(/packages/upload/src/upload.vue):
handleClick() { if (!this.disabled) { this.$refs.input.value = null; this.$refs.input.click(); }}
总结
如有谬误,欢送留言探讨。
参考资料
- file drag and drop API;
- upload组件GitHub地址;
- jpg和jpeg文件的区别。