JavaScript使用H5 FileReader和Image對象獲取本地圖片的分辨率

業務場景

試想這樣的應用場景:

當我們上傳本地圖片時,需要校驗圖片的分辨率,只有水平分辨率不大於1920、垂直分辨率不大於1080的圖片才允許上傳。

後端處理方案

顯然,我們是可以將分辨率校驗交給後端的,這樣的問題在於,後端校驗需要前端先發起ajax請求,並且後端校驗也需要一定的處理時間,就整體體驗而言,用戶等待時間較長,不利於用戶體驗。

因此還是推薦前端校驗,直接給出結果。

難點所在

對於type='file'的input框,在其change事件中,我們可以直接拿到file對象,包含了name、size等屬性,但是並不包含分辨率數據。因爲這時,圖片尚未渲染到dom中,瀏覽器不知道這是一個圖片,拿不到分辨率也可以理解。如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="file" accept="image/jpg,image/jpeg,image/png" id="fileInput" />
    <script>
      document
        .getElementById("fileInput")
        .addEventListener("change", handleFileChange, false);
      function handleFileChange(e) {
        console.log(e.target.files[0]);
      }
    </script>
  </body>
</html>

在這裏插入圖片描述

破局:使用FileReader轉換爲base64格式

雖然原生的file對象拿不到圖片的分辨率信息,但是藉助於FileReader則可以讀取到文件的內部數據,並轉換爲base64。

      function handleFileChange(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function () {
          if (reader.readyState == 2) {
            console.log(reader.result);
          }
        };
      }

在這裏插入圖片描述

突破:使用Image對象獲取分辨率

現在,已經拿到base64數據,那麼只需要創建一個Image實例,並將其src 設置爲base64數據地址,再監聽onload事件就可以拿到分辨率了,如下:

      function handleFileChange(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function () {
          if (reader.readyState == 2) {
            const img = new Image();
            img.src = reader.result;
            img.onload = function () {
              console.log(this.height, this.width);
            };
          }
        };
      }

在這裏插入圖片描述

時序:使用promise解決異步和同步問題

以上,我們雖然能夠拿到數據,但是,獲取分辨率的過程是異步的,例如我們監聽了reader.onload、img.onload事件。顯然,我們校驗突破分辨率時,需要等待這些過程執行完,才能給出結果。而對於圖片的校驗很可能是同時還需要校驗格式、文件大小,這些都被封裝在校驗函數中,而我們的校驗分辨率的函數則應屬於整個校驗過程的一部分
即,整個校驗函數中需要等待校驗分辨率函數的結果,這是一個同步的過程
這並不困難,我們只需要使用es6的promise即可比較容易的達成這一目標,寫法多樣,下面提供一種示例:

function validRatio(file) {
      return new Promise(resolve => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function() {
          if (reader.readyState == 2) {
            const img = new Image();
            img.src = reader.result;
            img.onload = function() {
              const bool = this.height > 1080 || this.width > 1920;
              if (bool) {
                resolve(false);
              }
              resolve(true);
            };
          }
        };
      });
    },
    async function validateFile(file) {
      //...
      const isRatioValid = await validRatio(file);
      //...
  }

如有其他更好的方案,歡迎指出!

全文完。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章