elementUI中upload組件使用總結

最近使用了elementUI提供的upload組件上傳文件,總結下使用過程當中的一些心得。本文不會介紹如何使用elementUI中的upload組件,由於官網自己就已經介紹的很詳細了。vue

本文主要包括如下幾個問題:git

  1. 文件校驗是如何作的,可否同時校驗多種文件格式;
  2. 上傳文件的時候可否自定義拖拽樣式;
  3. 點擊的時候如何實現手動上傳。

序言

  1. 本文涉及到源碼的部分,以v2.15.1爲例;
  2. upload組件源代碼在/packages/upload文件夾下面;

文件校驗是如何作的,可否同時校驗多種文件格式

upload組件提供了兩種上傳文件的方式:github

  1. input元素方式:經過input元素觸發系統的文件選擇窗口來選擇文件;
  2. 拖拽方式:經過原生drag和drop API來選擇文件。

針對上述兩種方式,upload組件提供了屬性accept限制校驗文件格式。這裏,我使用了兩個詞:瀏覽器

  1. 限制:從源頭層面上限制,也就是否是特定的格式就選擇不了,適用於input元素方式
  2. 校驗:從結果層面上校驗,也就是在最後一步才能發現選擇不了,同時適用於上述兩種方式,其中校驗又包括兩種:dom

    1. 源碼自己包含的校驗邏輯;
    2. 自定義校驗。

接下來咱們結合upload組件源碼來分析下實現原理。ide

限制

限制形式的主要原理就是原生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屬性的時候,系統的文件選擇框中的文件都是可選的:this

<input type="file" />

image.png

當添加了accept以後,好比.png,系統的文件選擇框中只有符合格式的文件纔是可選的:spa

<input type="file" accept=".png" />

image.png

如何支持多種文件格式上傳呢?看下原生input元素accept屬性的文檔,會發現accept是支持逗號分割的多種文件格式的:操作系統

<input type="file" accept=".png,.jpg,.jpeg" />

image.png
開發的時候發現一個有意思的問題,就是若是accept裏面.jpg和.jpeg只寫一個,另一種格式也是能夠選擇的:

<input type="file" accept=".png,.jpeg" />
// 或者
<input type="file" accept=".png,.jpg" />

image.png
因此,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文件的區別
相關文章
相關標籤/搜索