Ajax獲取圖片的兩種方式

在Web項目中,我們可能遇到需要利用Ajax來獲取圖片的情況。因爲客戶端處理的是圖片文件的二進制流,所以可利用Blob和File API來將圖片轉爲URL,賦值給img的src屬性來解決這個問題。本文總結Ajax獲取圖片的兩種方式,即針對XMLHttpRequest Level 1和Level 2給出解決方案。(注意:若無說明,下文中所有xhr都代表XMLHttpRequest 對象)。PS:最近想到這種需求,於是閱讀一些資料,整了一天,寫了本篇文章。如有錯誤,請不吝指正。題外話:寫博客貴在堅持呀!!!

  1. 首先給出html結構,因爲重點講Ajax,所以用一個很簡單的例子來說明,頁面只有一個button和一個img。

    <!--login.html的其他代碼省略 -->
    <input type="button" id="btn">
    <!--獲取的圖片將在此顯示-->
    <img id="preview">
  2. 因爲Ajax要和後臺交互,此處將使用Node.js完成後臺的邏輯,邏輯同樣很簡單。只需對//upload做處理即可。Ajax將請求/upload的內容。當請求時,發送給客戶端圖片。代碼如下:

    var http=require('http');
    var fs=require('fs');
    http.createServer((req,res)=>{
      console.log(`${req.method}:${req.url}`);
      if(req.url=='/'){
        res.writeHead(200,{'Content-Type':'text/html,charset=utf-8'});
        fs.createReadStream('login.html').pipe(res);
      }else if(req.url=='/upload'){
        if(req.method.toLowerCase()=='get'){
          fs.readFile('images/captcha.0953f.png',(err,file)=>{
            res.setHeader('Content-Length',file.length);
            console.log(file.length);
            res.writeHead(200,{'Content-Type':'image/png'});
            res.end(file);
          })
        }else{
          res.end('success');
          }
      }else{
          fs.readFile('.'+req.url,(err,file)=>{
          if(err){
            res.writeHead(404);
            res.end('Not found!');
          }
           fs.createReadStream('.'+req.url).pipe(res);
        })
      }
    }).listen(3000);
    
  3. Ajax請求圖片的兩種方式
    首先公共代碼如下:

    window.onload=function(){
      var btn=document.getElementById('btn');
      btn.onclick=function(){
      sendRequest();
     }
    }
    • XMLHttpRequest level 1(兼容舊瀏覽器):利用overrideMimeType()方法,將xhr設置爲xhr.overrideMimeType('text/plain;charset=x-user-defined');。其中text/plain表示把響應作爲普通文本來處理;charset=x-user-defined表示使用用戶自定義的字符集,以告訴瀏覽器不要去解析數據,直接返回未處理過的字節碼。此步驟必須,不可忽略。之後就可以通過xhr.responseText取得響應文本,然後將其轉爲二進制數據,傳遞給Blob構造函數。而後,通過FileReaderreadAsDataURL()方法創建URL賦值給圖片的src屬性即可。具體代碼如下:

      function sendRequest(){
          var xhr=new XMLHttpRequest();
          xhr.onload=function(){
            if(xhr.status>=200 && xhr.status<300){
              var binStr = this.responseText;
              //console.log(this.response==this.responseText);//true
              var arr = new Uint8Array(binStr.length);
              for(var i = 0, l = binStr.length; i < l; i++) {
      
                arr[i] = binStr.charCodeAt(i);
                //arr[i] = binStr.charCodeAt(i) & 0xff;
              }
              //console.log(binStr.charCodeAt(0).toString(16));
              //console.log(arr[0].toString(16));
              var blob=new Blob([arr.buffer],{type:'image/png'})
              loadImage_file(blob);
            }else{
              console.log('error');
            }
          }
          xhr.open('get','/upload',true);
          //兼容老瀏覽器。必須,以文本格式接受,字符集自定義
          xhr.overrideMimeType('text/plain;charset=x-user-defined');
          xhr.send(null);
        }
      function loadImage_file(blob){
          var fr=new FileReader();
          fr.readAsDataURL(blob);
          fr.onload=function(e){
            var preview=document.getElementById('preview');
            preview.src=e.target.result;
          }
        }

      該方法關鍵點是把響應文本轉爲二進制數據,利用字符串的charCodeAt(index)方法可以得到index位置的字符碼,遍歷整個字符串將其編碼保存至Uint8Array中,而後傳遞給Blob來重構URL。關於arr[i] = binStr.charCodeAt(i) & 0xff;,表示在每個字符的兩個字節之中,只保留後一個字節,將前一個字節扔掉。原因是瀏覽器解讀字符的時候,會把字符自動解讀成Unicode的0xF700-0xF7ff區段。但因爲我用的是Uint8Array,它會自動截取低8位,所以做不做與運算也就無所謂了。

    • XMLHttpRequest level 2:設置xhr的responseType屬性,指定爲blob(或ArrayBuffer,如設置爲ArrayBuffer,還要轉Uint8Array,然後轉成blob),例如,xhr.responseType='blob';。這樣就可以通過xhr.response來獲取該blob對象。注意不能用xhr.responseText來獲取,因爲該屬性只在響應類型是“text”時有效,強行獲取只會得到錯誤:Uncaught InvalidStateError: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').。該方法響應blob對象,所以無需對響應再做其他處理,只需傳給FileReaderreadAsDataURL()就可以了。代碼如下:

      function sendRequest(){
          var xhr=new XMLHttpRequest();
          xhr.onload=function(){
            if(xhr.status>=200 && xhr.status<300){
              var blob = this.response;
              //loadImage_file()方法與前面相同
              loadImage_file(blob);
            }else{
              console.log('error');
            }
          }
          xhr.open('get','/upload',true);
          xhr.responseType='blob';
          xhr.send(null);
        }

      從中可以看出,代碼量大大減少,語法簡潔而清晰。

總結:兩種方式各有各的有點,方法一兼容性好,方法二清晰簡潔。具體用那種,還看各位的想法了。
最後,給出幾篇關於XMLHttpRequest的優秀文章:
1. 你真的會使用XMLHttpRequest嗎?
2. XMLHttpRequest Level 2 使用指南

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