HTML5 實現手機拍照上傳

背景:移動端H5項目,須要實現調用手機拍照,並將圖片壓縮上傳功能。javascript

  1. 頁面樣式:
    1. 上傳圖片有原生的方法<input type="file" accept="image/*">,若是隻想要拍照上傳,不但願用戶選擇圖片上傳,能夠經過添加capture屬性,該屬性值有camcorder/microphone/camera...,此處選擇camera。PS:該屬性存在瀏覽器兼容性問題,不是全部的瀏覽器都支持。
      <input type="file" accept="image/*" capture="camera" >
    2. 由於原生file樣式不知足要求,須要進行相應的處理,此時使用定位,在input上面放置咱們想要的頁面效果。而後當點擊上面的元素,就能夠觸發咱們的input進行圖片上傳。此時的問題是:當點擊input上面的元素,須要事件穿透,即至關於點擊input。則藉助於css3新屬性pointer-events。
      //使用cursor進行事件穿透,來阻止元素成爲鼠標事件的目標
      button{
           cursor:pointer;
           pointer-events:none;
      }

      ----此時圖片上傳的樣式已經處理好了----css

      代碼片斷:
      <style >
          *{
              padding: 0;
              margin: 0;
          }
          .wrapper{
              width: 320px;
              height: 50px;
              margin: 20px auto;
              position: relative;
              border: 1px solid #f0f0f0;
          }
          input{
              width: 100px;
              height: 30px;
          }
          button{
              position: absolute;
              cursor: pointer;
              pointer-events: none;
              width: 100px;
              height: 30px;
              left: 0;
              top: 0;
          }
          a{
              pointer-events: none;
          }
          .img{
              border: 1px solid #ccc;
              padding: 10px;
          }
      </style >
      
      <div class = "wrapper">
           <input type = "file" accept= "image/*" capture= "camera" id= "img" />
           <button >上傳照片 </button >
      </div >
  2. 圖片壓縮處理:
    1. 由於要作的是手機拍照上傳,如今的手機拍照片都很大,好比小米4S,大小在3M以上,若是原圖上傳,太消耗用戶流量,因而要解決圖片壓縮的問題。
    2.   經過change事件,監聽圖片上傳,經過readerAsDataURL獲取上傳的圖片。html

      document.getElementById( 'img').addEventListener( 'change', function () {
           var reader = new FileReader();
           reader.onload = function (e) {
                //調用圖片壓縮方法:compress();
           };
           reader.readAsDataURL(this.files[0]);
           console.log(this.files[0]);
           var fileSize = Math.round( this.files[0].size/1024/1024) ; //以M爲單位
           //this.files[0] 該信息包含:圖片的大小,以byte計算 獲取size的方法以下:this.files[0].size;
      }, false);
    3.   對上傳的圖片進行壓縮,須要藉助於canvas API,調用其中的canvas.toDataURL(typeencoderOptions); 將圖片按照必定的壓縮比進行壓縮,獲得base64編碼。重點來了:壓縮策略:先設置圖片的最大寬度 or 最大高度,通常設置其中一個就能夠了,由於全部的手機寬高比差異不是很大。而後設置圖片的最大size,allowMaxSize,根據圖片的實際大小和最大容許大小,設置相應的壓縮比率。前端

      //最終實現思路:
      一、設置壓縮後的最大寬度 or 高度;
      二、設置壓縮比例,根據圖片的不一樣size大小,設置不一樣的壓縮比。
      
      function compress(res,fileSize) { //res表明上傳的圖片,fileSize大小圖片的大小
          var img = new Image(),
              maxW = 640; //設置最大寬度
      
          img.onload = function () {
              var cvs = document.createElement( 'canvas'),
                  ctx = cvs.getContext( '2d');
      
              if(img.width > maxW) {
                  img.height *= maxW / img.width;
                  img.width = maxW;
              }
      
              cvs.width = img.width;
              cvs.height = img.height;
      
              ctx.clearRect(0, 0, cvs.width, cvs.height);
              ctx.drawImage(img, 0, 0, img.width, img.height);
      
              var compressRate = getCompressRate(1,fileSize);
      
              var dataUrl = cvs.toDataURL( 'image/jpeg', compressRate);
      
              document.body.appendChild(cvs);
              console.log(dataUrl);
          }
      
          img.src = res;
      }
      
      function getCompressRate(allowMaxSize,fileSize){ //計算壓縮比率,size單位爲MB
            var compressRate = 1;
      
            if(fileSize/allowMaxSize > 4){
                 compressRate = 0.5;
            } else if(fileSize/allowMaxSize >3){
                 compressRate = 0.6;
            } else if(fileSize/allowMaxSize >2){
                 compressRate = 0.7;
            } else if(fileSize > allowMaxSize){
                 compressRate = 0.8;
            } else{
                 compressRate = 0.9;
            }
      
            return compressRate;
      }
  3. 圖片上傳給服務器:
    1. 圖片已經壓縮完成了,可是壓縮後的圖片不是File,而是一個base64編碼,因而乎兩個選擇:一、以String的形式將base64編碼上傳給服務器,服務器轉存base64爲img圖片;二、前端將base64轉爲File圖片類型,上傳給服務器。
    2. 方法一:壓縮以後直接上傳base64給後臺,實現簡單。
    3. 方法二:前端本身轉存base64位File圖片,這個對於前端 壓力比較大。

      緣由:html5 canvas屬於客戶端API,沒有權限去保存圖片到硬盤,只有canvas.toDataURL()這一接口可導出畫布的base64編碼,以提供給服務器進行處理保存。因此若是要將base64編碼轉成圖片須要藉助於nodeJs。由於nodeJS有相關文件處理的API。html5

      //使用nodeJS將base64轉化成圖片
      var express = require('express');
      var fs = require("fs");
      var app = module.exports = express();
      
      function dataToImage(dataUrl){
          var base64Data = dataUrl.replace(/^data:image\/\w+;base64,/,'');
          var dataBuffer = new Buffer(base64Data,'base64');
      
          fs.writeFile('out.jpg',dataBuffer,function(err){
              if(err){
                  console.log(err);
              }else{
                  console.log('Success...');
              }
          });
      }
      
      dataToImage('data:image/jpeg;base64,/9...'); //圖片完整base64過長,因此省略...
      
      if(!module.parent){
          app.listen(8000);
          console.log('Express started on port 8000');
      }

      Summary:若是使用nodeJS,須要單獨部署nodeJS代碼到服務器,整個邏輯會比較麻煩。綜合比較兩種方法,推薦使用第一種方法,直接傳base64給服務器,後臺處理相應的轉化!java

demo: http://www.zhangyixia.com/image-upload/  node

可直接掃描二維碼:
                               css3

相關知識科普:web

  1. 圖像通常由兩部分組成:一部分是數據自己,他記錄了每一個像素的顏色值,另外一部分是文件頭,這裏記錄着形如圖像的寬度,高度等信息。
  2. 不一樣圖片類型及適用場景:
    1. 不一樣圖片類型:
      1. GIF:無損壓縮,體積小,支持透明效果,缺點:色彩效果最低,用gif保存鮮豔的圖片的話會讓網站看上去很是可怕。
      2. PNG:無損壓縮,可漸變透明,缺點:不如JPG顏色豐富,一樣的圖片體積也比JPG略大。
      3. JPG/JPEG:色彩還原性好,能夠在照片不明顯失真的狀況下,大幅度壓縮圖片格式,因此體積不會很大。缺點:不支持透明
    2. 適用場景:
      1. 當圖片色彩鮮豔,基本沒有透明效果的時候,選擇jpg/jpeg。
      2. 當圖片色彩鮮豔,透明效果較多的狀況下,選擇png;
      3. 當圖片色彩比較單一狀況下,能夠選擇gif;
  3. toDataURL,查找原生壓縮圖片的方法。type支持image/webp格式
    canvas.toDataURL(type, encoderOptions);
  4. Base64編碼:將三個8Bit的字節轉換爲四個6Bit的字節。

 

參考資料:express

http://www.imys.net/20150916/webapp-input-use-camera.html

http://www.oxxostudio.tw/articles/201409/pointer-events.html

http://www.javashuo.com/article/p-kuvckgsr-gx.html

相關文章
相關標籤/搜索