圖片上傳知識點梳理

在平常項目開發中,圖片上傳是一個十分常見的場景。而如今的各類UI框架都提供了本身的上傳組件,網上第三方的上傳組件也多如牛毛。可能你早已習慣了直接使用這些現成的組件,然而對於其具體的實現,卻並未深刻解析。本文將經過簡單的代碼,爲你解析圖片上傳的各個知識點。web

樣式自定義

既然是上傳,確定須要使用到<input type="file">標籤了。然而,默認的input到標籤樣式不只單一,且在各個瀏覽器下的表現也不相同,因此一般須要對input進行樣式自定義。但<input>標籤對於樣式的修改並不十分友好。解決方法不少,最經常使用的是將<input type="file">標籤隱藏,而後經過一個<label>標籤進行關聯,而後直接修改<label>標籤的樣式來實現。代碼以下:ajax

<input type="file" id="uploadImg">
    <label for="uploadImg">點擊上傳</label>

圖片校驗

在上傳以前,通常會對文件進行各類校驗,例如文件類型,大小,格式,尺寸等。瀏覽器

其中文件類型,可經過設置<input>標籤的accept來指定文件類型。但accept屬性並不能徹底禁止用戶上傳指定類型以外的文件。故能夠經過上傳文件的各個屬性進行校驗攔截。校驗前,咱們須要經過change事件的事件對象來獲取到上傳的文件:服務器

event.target.files

能夠獲取到上傳文件列表。列表中對象包含以下信息:app

{
        lastModified: 1524153515000
        lastModifiedDate: Thu Apr 19 2018 23:58:35 GMT+0800 (中國標準時間) {}
        name: "589adfbfe821c.jpg"
        size: 1357444
        type: "image/jpeg"
        webkitRelativePath: ""
    }

從該對象中,咱們能夠獲取到文件大小,文件類型,文件名等信息,從而能夠在上傳以前對這些信息進行校驗,從而攔截掉不合法的文件。框架

然而,從file對象中,咱們並不能獲取圖片的尺寸信息。而在咱們的業務中,不少場景都須要限制上傳圖片的尺寸爲某一個固定值,或者是某一個比例。以減小後期顯示時的適配問題。要實現對上傳圖片尺寸對校驗,咱們須要使用到FileReaderImage異步

FileReader對象容許Web應用程序異步讀取存儲在用戶計算機上的文件。函數

Image()函數將會建立一個新的HTMLImageElement實例。它的功能等價於document.createElement('img')code

這裏,咱們須要用到fileReaderreadAsDataURL()方法來讀取上傳文件信息,經過onload處理事件來獲取讀取到的文件信息。以下:orm

const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = e => {
        console.log(e.target.result)
    }

代碼中,file爲咱們以前獲取到的文件列表files中的文件對象。e.target.result爲讀取到到文件內容。

以後經過new Image()函數建立一個新的HTMLImageElement實例,並將該實例的src賦值爲fileReader讀取到到文件內容。便可獲得一個該文件的HTMLImageElement實例,經過該實例,咱們即可以讀取到該圖片的尺寸信息。具體代碼以下:

const image = new Image();
    image.src = e.target.result;
    image.onload = () => {
        console.log(image.width, image.height);
    }

圖片上傳預覽

在以前的開發中,圖片上傳顯示一般會採用先將文件上傳,預覽圖片直接展現上傳到服務器中到圖片來實現,但這樣沒法達到上傳前預覽該圖片的目的,且會形成許多垃圾圖片的上傳。

經過前面對於獲取圖片尺寸研究。相信你能很快想到一種更加優雅的圖片預覽方案,既然咱們已經獲取到了該文件的HTMLImageElement實例,那麼咱們直接將該實例append到頁面的容器Dom中不久行了。或者直接將獲取到的文件設置到已存在的image標籤的src屬性中。圖片上傳預覽就是這麼簡單。

圖片上傳與上傳進度展現

圖片的上傳,咱們能夠直接經過form標籤搭配表單的submit()方法來實現圖片的上傳。然而,這樣咱們就沒法在上傳前進行上傳文件的校驗與攔截。同時須要用戶主動觸發提交操做。要想讓咱們以前作的上傳前的攔截工做不白作,咱們須要去在合適的時候,主動觸發文件的上傳操做。

這裏,須要使用到FormData對象,來將入參對象數據轉爲表單數據。

FormData對象用以將數據編譯成鍵值對,以便用XMLHttpRequest來發送數據。其主要用於發送表單數據,但亦可用於發送帶鍵數據(keyed data),而獨立於表單使用。若是表單enctype屬性設爲multipart/form-data ,則會使用表單的submit()方法來發送數據,從而,發送數據具備一樣形式。

首先咱們建立一個formData對象,而後經過append() 方法來添加字段。以下:

const formData = new FormData();
    formData.append("file", file);

注意,formData雖然爲一個對象,但經過console.log卻沒法打印出其具體的值,只會獲得FormData {}

接下來建立一個XMLHttpRequest對象,用來發送ajax請求。而且經過該XMLHttpRequest對象的upload.onprogress方法,能夠實時獲取到上傳信息,並進一步獲取到上傳的進度。具體代碼以下:

const client = new XMLHttpRequest()
    client.open("POST", uploadUrl)
    client.upload.onprogress = function(e) {
      if (e.lengthComputable) {
        let total = e.total;
        let loaded = e.loaded;
        let percentage = parseFloat(loaded / total).toFixed(2);
      }
    }
    client.send(formData)

上面代碼中,uploadUrl爲上傳的URL。經過upload.onprogress的事件對象,能夠獲取到當前進度已上傳的文件大小以及完整文件大小,經過這兩個大小參數,能夠很容易計算出已上傳文件的比例,以後是顯示上傳進度條、仍是展現進度數據,就能夠隨意操做了。

拖拽上傳

除了傳統的點擊選擇上傳文件外,拖拽文件上傳也是一個十分常見的場景。要使用拖拽上傳,就須要使用H5的拖放方法dropdrag方法。除了這兩個主要的方法外,還有拖放的不一樣階段觸發的多個方法,經常使用的拖拽方法以下:

  • ondragstart 事件:當拖拽元素開始被拖拽的時候觸發的事件(做用對象爲被拖曳元素)
  • ondrag:在元素拖動期間不停的觸發該事件,與touchmove事件相似。(做用對象爲被拖曳元素)
  • ondragend 事件:當拖拽完成後觸發的事件(做用對象爲被拖曳元素)
  • ondragenter 事件:當拖曳元素進入目標元素的時候觸發的事件(做用對象爲目標元素)
  • ondragover 事件:拖拽元素在目標元素上移動的時候觸發的事件(做用對象爲目標元素)
  • ondragleave 事件:拖拽元素在目標元素上移動的時候觸發的事件(做用對象爲目標元素)
  • ondrop 事件:被拖拽的元素在目標元素上同時鼠標放開觸發的事件(做用對象爲目標元素)

拖拽的各個事件相似與touch事件的各個階段。然而須要注意的是,拖拽的各個事件,有着本身的做用對象,做用對象分爲‘被拖拽元素’和‘目標元素’。被拖拽元素 爲拖拽的那個Dom元素,主要使用在頁面內Dom拖拽移動的場景。目標元素爲接收被拖拽元素的元素區域。當被拖拽元素進入到該區域,便會觸發目標對象的一系列事件。

在圖片拖拽上傳這個業務場景中,被拖拽元素爲頁面外部的圖片文件,故此處僅用到目標元素的各個事件。咱們能夠經過這些事件來修改目標區域樣式等。核心的兩個事件爲ondragoverondrop事件。可能你以爲我只須要在鬆開鼠標時獲取拖拽的文件就行,所以只須要使用ondrop事件就好了?可是,因爲瀏覽器的默認行爲,ondrop事件並不會被觸發。所以,須要使用e.preventDefault(); 來阻止掉 ondropover的瀏覽器默認事件,從而保證ondrop事件的觸發。經過ondrop事件的事件對象,咱們能夠獲取到跟event.target.files相同的文件列表,獲取方法爲event.dataTransfer.files;然而,當你這麼寫完以後,進行拖拽以後,你會發現瀏覽器自動跳轉到了該圖片的預覽頁。這也是因爲瀏覽器的默認行爲致使,所以也須要使用e.preventDefault();來阻止掉瀏覽器的默認行爲。這樣,即可以進行後續的文件校驗操做來。
具體實現代碼以下:

<label for="uploadImg"
        onDragOver={e => {
            e.preventDefault();
        }}
        onDrop={e => {
            if (e.dataTransfer) {
                e.preventDefault();
                const file = e.dataTransfer.files[0];
                ...
            } 
        }}
    </label>

至此,圖片上傳的經常使用知識點以梳理完畢,歡迎補充。

相關文章
相關標籤/搜索