圖片爲blob形式能作什麼?

  • 感悟源頭

前幾天讀到掘金大神一篇有深度的文章 爲何視頻網站的視頻連接地址是blob?javascript

讓kk對以前在業務上使用過的blob對象有了深刻的瞭解。因此有感而發,也想要整理一下在以前的場景下,如何運用blob對象對圖片進行轉碼、上傳等操做。html

  • 什麼是blob

kk查詢了 MDN 對blob的定義前端

Blob 對象表示一個不可變、原始數據的類文件對象。Blob 表示的不必定是JavaScript原生格式的數據。File 接口基於Blob,繼承了 blob 的功能並將其擴展使其支持用戶系統上的文件。java

基於此定義的業務思考,kk可聯想到 File對象 就是日常在form表單post形式上傳圖片文件的所對應的的形式。既然File基於blob,那麼post方法上傳一個blob對象也是可行的。web

  • 業務場景

如上圖爲身份證識別+視頻活體檢測在H5端實現的一個需求。在用戶拍照上傳身份證時候,前端要對該圖片進行壓縮、轉碼並上傳,與此同時保證上傳的性能和後端OCR算法的識別精度。算法

這裏順便想說起下:這個項目在剛開始的時候,上傳的性能比較差,緣由是上傳的圖片爲原圖,容量很大致使上傳時間太長,影響了用戶體驗。canvas

你們也知道,在微信裏發送原圖也是須要必定時間和資源的,在H5端也不例外。後端

通過反覆試驗,iOS的手機(kk試驗的是iPhone 7 Plus 後置1200萬像素),上傳的原圖容量爲3M,換在最新發布的安卓手機上,狀況就變得更加惡劣。設計師那臺最新發布的榮耀V20,2000萬像素,發送一張原圖高達8M,這就尷尬了。這勢必要在H5端上傳以前對圖片進行壓縮。瀏覽器

要用到blob的地方就在於壓縮事後,圖片轉碼並上傳的過程。事不宜遲,上代碼。服務器

  • 功能實現

功能實現主要分爲以下四大塊

  • 圖片選取
  • 圖片壓縮
  • blob轉碼
  • 圖片上傳
    1. 圖片選取

preview(event) {
      var _this = this;

      const eventPath = event.path || (event.composedPath && event.composedPath());
      // 當選取一張圖片時,新版iOS Safari瀏覽器會將圖片存儲在composedPath裏,或者從composedPath方法獲取,而普通瀏覽器依然會存放於path裏
      var domId = eventPath[0].id;
      var cardType = _this.cardTypeJudge(eventPath[0].id);
      
      let files = document.getElementById(eventPath[0].id).files[0];

      if (files.size / 1048576 > 15) {
       
        _this.confirmShow = true;
        _this.confirmReason = '圖片容量過大,請從新拍攝';
        document.getElementById(domId).value = null
        return false;
      }
      _this.smImage(files).then(resone => {
  
        //上傳圖片
        _this.uploadFile(resone, cardType).then(restwo => {
          // console.log('restwo:' + restwo);
          document.getElementById(eventPath[0].id).value = null;
          _this.handleIdCardUpdateSuccess(_this.app.idCardFrontSrc, _this.app.idCardBackSrc, 1, cardType);
          _this.submitBtnStatus();
        });
      });
    },
複製代碼
  • 解析

在用戶從系統相冊選定圖片後,H5須要獲取觸發事件元素冒泡過程的全部元素,即點擊選擇的圖片地址。

通過kk分析,在Chrome中能夠經過event.path獲取,Firefox和Safari中發現event並無path屬性,確實須要調用event.composedPath()方法或者從event.composedPath取得。
這樣才能保證H5在各移動端瀏覽器的功能兼容性。

Event.composedPath() 是瀏覽器一個新的標準,沒想到移動端的更新步伐那麼快。

以下是kk參考其餘做者整理的獲取圖片元素地址的方法:

element.onClick(event) {
  const ev = window.event || event;
  const path = event.path || (event.composedPath && event.composedPath());
  console.log(path)  //[button#btn, div, body, html, document, Window]
}
複製代碼
    1. 圖片壓縮

smImage(files) {
      var _this = this;
      return new Promise((resolve, reject) => {
        // 壓縮圖片須要的一些元素和對象
        var reader = new FileReader(),
          //建立一個img對象
          img = new Image();

        // 縮放圖片須要的canvas
        var canvas = document.getElementById("imgcanvas");;
        var context = canvas.getContext('2d');

        // base64地址圖片加載完畢後
        img.onload = function () {
          // 圖片原始尺寸
          var originWidth = this.width;
          var originHeight = this.height;
          // 最大尺寸限制,可經過國設置寬高來實現圖片壓縮程度
          var maxWidth = 1300,
            maxHeight = 1300;
          // 目標尺寸
          var targetWidth = originWidth,
            targetHeight = originHeight;
          // 圖片尺寸超過1300x1300的限制
          if (originWidth > maxWidth || originHeight > maxHeight) {
            if (originWidth / originHeight > maxWidth / maxHeight) {
              // 更寬,按照寬度限定尺寸
              targetWidth = maxWidth;
              targetHeight = Math.round(maxWidth * (originHeight / originWidth));
            } else {
              targetHeight = maxHeight;
              targetWidth = Math.round(maxHeight * (originWidth / originHeight));
            }
          }
          // canvas對圖片進行縮放
          canvas.width = targetWidth;
          canvas.height = targetHeight;
          // 清除畫布
          context.clearRect(0, 0, targetWidth, targetHeight);
          // 圖片壓縮
          context.drawImage(img, 0, 0, targetWidth, targetHeight);
          /*第一個參數是建立的img對象;第二個參數是左上角座標,後面兩個是畫布區域寬高*/
          //壓縮後的圖片base64 url
          /*canvas.toDataURL(mimeType, qualityArgument),mimeType 默認值是'image/jpeg'; * qualityArgument表示導出的圖片質量,只要導出爲jpg和webp格式的時候此參數纔有效果,默認值是0.92*/
          var newUrl = canvas.toDataURL('image/jpeg', 0.5); //base64 格式
          
          var blob = _this.convertBase64UrlToBlob(newUrl);
          // console.log(blob);
          // console.log('canvas寬度:' + targetWidth + ' 長度:' + targetHeight);
          if (targetWidth < targetHeight) {
            _this.isLongImg = true;
          } else {
            _this.isLongImg = false;
          }
          resolve(blob);
        };

        // 文件base64化,以便獲知圖片原始尺寸
        img.src = _this.app.getFilePath(files);
      })
    }
複製代碼
  • 解析

圖片壓縮整體採用canvas重繪的方法,對圖片的尺寸和分辨率兩方面壓縮,其閾值經過canvas的各類API可實現人工調整。

以上所述,重繪大體分爲兩步:尺寸限定和壓縮繪製。

  • 尺寸限定

先建立一個canvas畫布,並限制其畫布寬高。kk將畫布寬maxWidth高maxHeight閾值都限定在了1300px,同時取得圖片的原始寬originWidth高originHeight進行比較。

當width和height縮減到同比例下低於寬高限定閾值時,則根據畫布寬高drawImage重繪。

  • 壓縮繪製

主要取決於canvas.toDataURL() 方法中。當圖片重繪至畫布後,經過此方法可輸出一個Data URI,即默認爲png的base64串。

壓縮的比例,圖片輸出的分辨率就取決於這個方法的第二個參數:encoderOptions 。

encoderOptions

調整這個參數就能夠導出不一樣分辨率的base64串,kk在這裏調整的是0.5,即50%分辨率的壓縮。

    1. blob轉碼

上面kk也說起過,使用post方法上傳文件,會取得一個File對象(基於blob),並做爲二進制流(binary)上傳到服務端。

如今重繪的圖片呈現的形式是base64,則須要一個base64轉blob的方法。

convertBase64UrlToBlob(urlData) {
      var arr = urlData.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], {
        type: mime
      });
    }
複製代碼

這個方法目前是怎麼進行轉碼的kk還沒弄清楚,只是做爲Util工具類一直運用在該項目裏,後期弄懂後會更新。

    1. 圖片上傳

    1. 閾值調整

在此功能流程中,可調整的閾值有:

  • 圖片尺寸壓縮的臨界值:maxWidth 和 maxHeight(本質是canvas畫布的尺寸)
  • 導出圖片的分辨率:本質是canvas.toDataURL()的參數設置

閾值調整後,性能相比於原來提高了50%,同時也保證了文字識別OCR的精度(實際上算法精度對圖片分辨率的要求並不須要原圖那麼高,因此進行適當的壓縮並不影響)。

性能提高50%體如今兩方面:功能提高和體驗提高。

  • 功能提高

原圖容量由8M及以上壓縮至2M如下,iOS原圖更是壓縮至1M如下,容量壓縮了70%左右。

  • 體驗提高

圖片上傳速度由原來的8.0s左右,提高至如今的2.0s~2.6s(3s之內,也取決於當時的網絡情況和服務器的接口響應)。

結語

這是我對這個項目開發經驗的整理,以及blob對象運用的一些我的理解。望指正!

相關文章
相關標籤/搜索