Ajax获取图片的两种方式

在Web项目中,我们可能遇到需要利用Ajax来获取图片的情况。因为客户端处理的是图片文件的二进制流,所以可利用Blob和File API来将图片转为URL,赋值给img的src属性来解决这个问题。本文总结Ajax获取图片的两种方式,即针对XMLHttpRequest Level 1和Level 2给出解决方案。(注意:若无说明,下文中所有xhr都代表XMLHttpRequest 对象)。PS:最近想到这种需求,于是阅读一些资料,整了一天,写了本篇文章。如有错误,请不吝指正。题外话:写博客贵在坚持呀!!!

  1. 首先给出html结构,因为重点讲Ajax,所以用一个很简单的例子来说明,页面只有一个button和一个img。

    
    <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 使用指南

你可能感兴趣的:(web,javascript)