最近应用了elementUI提供的upload组件上传文件,总结下应用过程中的一些心得。本文不会介绍如何应用elementUI中的upload组件,因为官网自身就曾经介绍的很具体了。

本文次要包含以下几个问题:

  1. 文件校验是如何做的,是否同时校验多种文件格式;
  2. 上传文件的时候是否自定义拖拽款式;
  3. 点击的时候如何实现手动上传。

序言

  1. 本文波及到源码的局部,以v2.15.1为例;
  2. upload组件源代码在/packages/upload文件夹上面;

文件校验是如何做的,是否同时校验多种文件格式

upload组件提供了两种上传文件的形式:

  1. input元素形式:通过input元素触发零碎的文件抉择窗口来抉择文件;
  2. 拖拽形式:通过原生drag和drop API来抉择文件。

针对上述两种形式,upload组件提供了属性accept限度校验文件格式。这里,我应用了两个词:

  1. 限度:从源头层面上限度,也就是不是特定的格局就抉择不了,实用于input元素形式
  2. 校验:从后果层面上校验,也就是在最初一步能力发现抉择不了,同时实用于上述两种形式,其中校验又包含两种:

    1. 源码自身蕴含的校验逻辑;
    2. 自定义校验。

接下来咱们联合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。感兴趣的能够参考这篇文章。

所以,

  1. 只通过accept的模式限度文件类型有时候可能不精确;
  2. Windows零碎可能会有兼容性问题,也就是即便提供了accept属性,其余格局的文件也是可选的;

这个时候就只能依赖上面的校验形式了。

源码自身蕴含的校验逻辑

当应用拖拽形式上传文件的时候,是能够拖拽任何文件的,所以只能在拖拽实现的时候去校验文件格局。那么,应该去校验哪些格局呢?从upload组件使用者的角度来说,无论是input元素形式还是拖拽形式,都应该是放弃对立的。也就是,原生input的accept属性反对的格局,在应用拖拽形式的时候,也都得反对。

所以,先看下input的accept属性反对的格局类型:

  1. 文件扩展名模式,以.结尾;
  2. MIME类型;
  3. "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();  }}

总结

如有谬误,欢送留言探讨。

参考资料

  1. file drag and drop API;
  2. upload组件GitHub地址;
  3. jpg和jpeg文件的区别。