美文网首页
处理 ajax 请求 excel

处理 ajax 请求 excel

作者: 小杺 | 来源:发表于2018-12-12 21:03 被阅读9次

    我们复现一下整个开发,与遇到的问题,及解决方式

    概述

    我们先看一下代码实现,然后逐步分析代码:

    const XHRCallback = function () {
            if (XHR.readyState == 4) {
                if (XHR.status == 200) {
                    if (!~XHR.getResponseHeader('Content-type').indexOf('json')) {
                        //no exist json
                        let resBlob = XHR.response;
                        const _name = decodeURI(XHR.getResponseHeader('Content-Disposition').split('-')[1];
                        const url = e.target.result;
                        const a = document.createElement('a');
                        a.download = _name;
                        a.href =  URL.createObjectURL(url);
                        a.click();
                    } else {
                        const reader = new FileReader();
                        reader.onload = function (e) {
                            const _transferData = e.target.result;
                            const _res = JSON.parse(_transferData);
                            //prompt _res.errmsg
                        };
                        reader.readAsText(XHR.response, 'UTF-8');
                    }
                } else {
                    //prompt no-200 error
                }
            } 
    };
    
    const XHR = new XMLHttpRequest();
    XHR.onreadystatechange = XHRCallback;
    XHR.open('GET', 'manage/export', true);
    XHR.responseType = 'blob';
    XHR.send();
    

    代码 p-1

    Q1: 怎么设置返回值类型

    当返回数据类型为application/vnd.ms-excel时,我们需要将这种返回值作为二进制处理。
    可以通过在xhr.send()前,设置xhr.reponseType = 'blob'即可。这时返回的数据即为blob类型。

    需要注意:responseType 设置成 blobarraybuffer 后,responseText 、responseXML 就取不到了

    Q2: responseType 是什么,怎么配置?

    responseType定义了响应数据的类型。

    The XMLHttpRequest property responseType is an enumerated string value specifying the type of data contained in the response. It also lets the author change the response type. If an empty string is set as the value of responseType, the default value of "text" is used.

    responseType不能再loading和已经返回result后配置,是没有用的,需要在send之前配置。

    返回的应该是流,你用let blob = new Blob([xhr.response], {type:'image/jpeg')来构造成Blob,然后图片的src用url.createObjectURL (blob)来读取,一般这样没问题了。如果未解决,ajax请求的图片后台返回的content-type未设置的话默认是image形式,你可以再设置xhr.responseType为'blob'或arraybuffer。确保数据是Blob。
    作者:KennyWu
    链接:https://www.zhihu.com/question/66477284/answer/242991929

    Value Description
    "" An empty responseType string is treated the same as "text", the default type (therefore, as a DOMString.
    "arraybuffer" The response is a JavaScript ArrayBuffer containing binary data.
    "blob" The response is a Blob object containing the binary data.
    "document" The response is an HTML Document or XML XMLDocument, as appropriate based on the MIME type of the received data. See HTML in XMLHttpRequest to learn more about using XHR to fetch HTML content.
    "json" The response is a JavaScript object created by parsing the contents of received data as JSON.
    "text" The response is text in a DOMString object.

    Q3: 用 FileReader 也可以处理 Blob 类型

    const XHRCallback = function () {
        if (XHR.status == 200) {
            if (XHR.readyState == 4) {
                const resBlob = XHR.response;
                const reader = new FileReader();
                reader.on = function (e) {
                    const url = e.target.result;
                    const a = document.createElement('a');
                    a.href = url;
                    a.click();
                };
                reader.readAsDataUrl(resBlob);
            }
        }
    };
    

    代码 3-1

    但是用 FileReader.readAsDataUrl 会处理成 base64 编码 (形式如:data:application/vnd.ms-excel;base64,)对于比较小的文件,没有问题,但是对于比较大的文件,会出现文件编译后,字符串对于 href 长度过长的问题

    下面是来自Google的两个回答:

    Is there a way to get around the limit?
    Very hardly.
    It is even probable that the limitations vary from browser to browser, or from E-Mail client to E-Mail client.
    I would rather use a HTML form and a server-side script to send the message.

    Yes, there is a limit on the length of the URL.
    The limit varies from browser to browser, so you should keep the URL below 2000 characters to be safe.
    Internet Explorer seems to be the browser that is having the shortest limit. According to this article it’s 2083 characters.

    对于 a 标签 href 属性的长度对于不同浏览器是有不同的限制的

    Q4: 如何最优化处理来自 server 的不同数据类型

    先来看一下 readyState 不同状态码对应的含义:

    Value State Description
    0 UNSENT Client has been created. open() not called yet.
    1 OPENED open() has been called.
    2 HEADERS_RECEIVED send() has been called, and headers and status are available.
    3 LOADING Downloading; responseText holds partial data.
    4 DONE The operation is complete.
    const XHRCallback = function () {
            if (
                XHR.readyState == 4 &&
                XHR.status == 200
                ) {
                const resBlob = XHR.response;
                const _name = decodeURI(XHR.getResponseHeader('Content-Disposition').split('-')[1];
                const reader = new FileReader();
                reader.on = function (e) {
                    const url = e.target.result;
                    const a = document.createElement('a');
                    a.download = _name;
                    a.href = url;
                    a.click();
                };
                reader.readAsDataUrl(resBlob);
            } else if (XHR.readyState == 2) {
                if (!!~XHR.getResponseHeader('Content-type').indexOf('json')) {
                    //exist json
                    //has capture the head info 
                    //prompt client the json message (这里只能提示用户前端自定义错误信息,而不能得到服务器端返回的错误信息)
                    XHR.abort();
                }
            //会在 readyState == 2 时候得到 status, 之前 status = 0
            } else if (XHR.readyState == 2 && XHR.status !== 200) {
                //prompt client the xhr failure
                XHR.abort();
            }
    };
    

    代码 4-1

    上面的代码看似正确,但是有一个错误在里面,就是如果在 readyState == 2 中是获取不到服务器端返回的结果的,结果只有在 readyState == 4 && status == 200 中才能返回。这里虽然高效,但是只能返回给用户前端自定义的错误结果。

    status 会在 readyState == 2 时候得到,在这之前 status == 0

    所以在这里,我们选择了 代码 p-1 中的形式,因为 json 返回结果并不是很多,数据量不大的情况下选择第一种处理方式还是合理的,如果错误信息的数据量同样很大,那么在 readyState == 2 时中止处理也是合理的,需要根据实际情况选择处理方式。

    Q5: XHR.abort() 是什么意思?它的工作原理是怎么样的?

    先来看一段来自于 MDN 的原话:

    The XMLHttpRequest.abort() method aborts the request if it has already been sent. When a request is aborted, its readyState is changed to XMLHttpRequest.UNSENT (0) and the request's status code is set to 0.

    这段话的翻译是:在执行这个指令后,readyState 会被重置为 0status 被重置为 0
    但是实际中发现,上面的代码 (4-1)中,直接将 readyStatestatus 设置成了 40,然后执行一遍 onreadystatechange 的回调函数,然后 readySate 会被设置成 0

    结论:在实际中(chrome),不论在何处调用 abort (在 send 后直接调用,或在回调函数中调用),都会将 readyState 设置成 4,并以 4 执行一遍回调函数,这是为什么呢?我们来做一下测试,看看
    XMLHttpRequstabort 到底是如何工作的。

    fragment 1:

    const XHRCallback = function () {
            console.log(XHR.readyState);
    };
    
    const XHR = new XMLHttpRequest();
    XHR.onreadystatechange = XHRCallback;
    XHR.open('GET', 'manage/export', true);
    XHR.responseType = 'blob';
    XHR.send();
    console.log('open has executed!');
    

    代码 5-1

    输出结果:

    1
    open has executed!
    2
    3
    4
    

    结论:

    可以看出 open 是同步的,立马执行回调函数,而 send 是异步的

    fragment 2:

    const XHRCallback = function () {
            console.log(XHR.readyState, XHR.status);
    };
    
    const XHR = new XMLHttpRequest();
    XHR.onreadystatechange = XHRCallback;
    XHR.open('GET', 'manage/export', true);
    XHR.responseType = 'blob';
    XHR.send();
    XHR.abort();
    console.log('open has executed!');
    

    代码 5-2

    输出结果:

    1 0
    4 0
    open has excuted!
    

    结论:

    abort 是同步操作,它会迅速组织后面的所有异步操作,并且将 readyStatestatus 设置成 40,然后调用一下回调函数,再将 readyState 设置成 0

    fragment 3:

    const XHRCallback = function () {
            console.log(XHR.readyState, XHR.status);
            if (XHR.readyState == 2) {
                XHR.abort();
                console.log(XHR.readyState, XHR.status);
            }
    };
    
    const XHR = new XMLHttpRequest();
    XHR.onreadystatechange = XHRCallback;
    XHR.open('GET', 'manage/export', true);
    XHR.responseType = 'blob';
    XHR.send();
    console.log('open has executed!');
    

    代码 5-3

    输出结果:

    1 0
    open has executed!
    2 200
    4 0
    0 0
    

    结论:

    再一次验证了上述的几个结论

    Q6: FileReader 的 readAsText 是什么?为什么不用 readAsBinaryString

    MDN 对于 readAsBinaryString 的定义如下:
    the result attribute contains the raw binary data from the file.

    而对 readAsText 的定义则是:
    and the result attribute contains the contents of the file as a text string.

    instanceOfFileReader.readAsText(blob[, encoding]);
    

    我们发现,readAsText 可以设置编码种类,当有中文的时候,readAsBinaryString 会出现乱码,而 readAsText 设置了 UTF-8 则不会。

    Q7: FileReader 中的 onload 和 onloadend 的区别


    参考文献:
    Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'


    可以参考上述文章

    Q8: ajax 中怎么设置前端超时?

    看如下一段代码

    //终止请求和超时
    function timeedGetText(url,timeout,callback){
          var xhr = new XMLHttpRequest();
          var timedout = false;
          var timer = setTimeout(function(){
                   timedout = true;
                   xhr.abort();
          },timeout);
          xhr.onreadystatechange = function(){
             if(xhr.readyState !=4){ return;}
             if(timedout){ return;}
             clearTimeout(timer);
             if(xhr.status === 200){
               callback(xhr.responseText);
             }
          };
    }
    

    Q9: 手动触发 dom 原生事件的方法

    除了 click 这样的方法,或许我们还有别的方法

    const save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
    const ev = document.createEvent("MouseEvents");
    ev.initMouseEvent(
        "click", true, false, window, 0, 0, 0, 0, 0
        , false, false, false, false, 0, null
    );
    save_link.dispatchEvent(ev);
    

    相关文章

      网友评论

          本文标题:处理 ajax 请求 excel

          本文链接:https://www.haomeiwen.com/subject/eorphqtx.html