最近应用了 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 文件的区别。