寫在前面 :從提出需求到完美的解決問題,實現過程是曲折的。
需求:在前(web client)後(Restful Service)端完全解耦的模式框架下,webclient需要請求 Service 返回的圖片文件(二進制流),並在client端顯示。
第一步思考:拿到此需求, 基於程序員的狂妄心裡,思考到顯示圖片而已,jquery ajax直接get請求 將返回data 賦值給img標簽的src屬性即可嘛,so easy~
不知天高地後的小子開始碼代碼,經過幾分鐘給出了以下的代碼,並自信滿滿的准備測試。
//$.ajax({ // method: "GET", // url: serverUrlBase + "/server/images/" + mapid + "/files/png",//跨域,請求會返回流文件 png圖片 // beforeSend: function (xhr) { // xhr.setRequestHeader("client_type", "DESKTOP_WEB"); // xhr.setRequestHeader("desktop_web_access_key", _desktop_web_access_key); // }, // success: function (data) { // //$("#remoteimg").attr("url", data); // } //});
在visual studio 中選中文件 在浏覽器中查看。
這是個什麼~!!!!!!!!!!!!!!! 不符合期望啊~~~ !!!!!!!!!
第二步思考:後端那小子給的API肯定有問題。先用工具測試看看。
打開chorme ,打開裝好的postman組件,輸入請求地址,點擊SEND。等待兩秒鐘一副超大的圖片文件顯示出來了~~
第三步思考:後端那小子的接口是正常的。 問題在我自己身上(表情漸漸嚴肅。) 把請求到的數據 console.log(data) 一下。
經過幾分鐘卡頓,我勒個去,浏覽器怎麼卡死了。耐心等了一會 數據打印出來了:
這是什麼乖乖,我滴孩嘞(正統 淮南話)。
第四步思考:怎麼是這樣的數據。Postman 中返回的是圖片啊 我的怎麼這樣,對了 看看postman 返回的是什麼數據。
在postman中選中圖片 -》右鍵-》檢查 如下圖所示:
第五步思考:為什麼我的數據與postman數據不同。為何是亂碼咧? 不對,ajax返回的數據是什麼?難道編碼被改了?難道不支持二進制流?
一邊想著 一邊打開jquery 官網(抱怨一下 jquery官網真心慢,w3school真心膚淺) 查找ajax datatype數據類型
發現竟然沒有二進制數據選項,那是不是返回的數據被默認以文本形式返回了。
抱怨:jquery做了這麼久了 一個ajax方法還停留在幾年前的xmlhttprequest 1的版本中,驚人的不支持流文件!!!
我這還怎麼大肆推行我的前後台完全隔離思想~~。算了不抱怨了,果然是不能靠別人,只能自己寫了。
樓主依稀記得 xmlhttprequest 2 標准中支持流文件的,並且應該使用 xhr.response作為返回 而不是responseText。 那麼開始寫到:
var url = serverUrlBase + "/server/images/" + mapid + "/files/png";'
var xhr = new XMLHttpRequest(); xhr.open('GET', url, true);//get請求,請求地址,是否異步
xhr.setRequestHeader("client_type", "DESKTOP_WEB"); xhr.setRequestHeader("desktop_web_access_key", _desktop_web_access_key);
xhr.onload = function() { if (this.status == 200) { var data = this.response;
//TODO...............................................如何接收數據呢。
} } xhr.send();
第六步思考: 這樣應該可行,但是怎麼處理請求的數據呢? ???? 這個問題。 對了 html5新特性裡面是不是 提到一個 Blob對象來著。試試看。
通過查閱相關blob資料,查閱 4.6.9 The response
attribute 得知 返回類型應該使用
在請求成功的地方 添加以下代碼:
var blob=new Blob();
blob=this.response;
既然二進制數據拿到了,那麼要把它放在一個 html標簽中,並且應該是img標簽 那麼:代碼應該是
var img = document.createElement("img");
img.src = window.URL.createObjectURL(blob); //有問題,將blob加載到img中 由於blob太大 會有性能影響 應該怎麼在加載之後 如何釋放呢:
img.onload = function(e) {
window.URL.revokeObjectURL(img.src);//釋放。
};
然後 將img 放到一個div容器中就可以啦。
$("#imgcontainer").html(img); 是的請求處理就應該是這樣。
那麼我們最終的代碼如下:
var url = serverUrlBase + "/server/images/" + mapid + "/files/png"; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = "blob"; xhr.setRequestHeader("client_type", "DESKTOP_WEB"); xhr.setRequestHeader("desktop_web_access_key", _desktop_web_access_key); xhr.onload = function() { if (this.status == 200) { var blob = this.response; var img = document.createElement("img"); img.onload = function(e) { window.URL.revokeObjectURL(img.src); }; img.src = window.URL.createObjectURL(blob); $("#imgcontainer").html(img); } } xhr.send();
結語:這樣樓主解決了 加載二進制流的問題。 結合 上一篇提到的 ajax跨域請求,對於前後端完全分離理念的實現又更近了一步。當然面對安全還是有許多要考慮的問題。
在此過程中,也讓摟著領悟到一點:高階的封裝(ajax)固然好,然而對一些特殊的請求無法處理(請求流文件),因此還需掌握底層的原理,才能面對苛刻的需求。
總結代碼:
var xhr = new XMLHttpRequest(); xhr.open("get", url, true); xhr.responseType = "blob"; xhr.onload = function() { if (this.status == 200) { var blob = this.response; var img = document.createElement("img"); img.onload = function(e) { window.URL.revokeObjectURL(img.src); }; img.src = window.URL.createObjectURL(blob);
$("#imgcontainer").html(img);
} } xhr.send();
參考資料:
http://api.jquery.com/jQuery.ajax/ jquery ajax api
https://xhr.spec.whatwg.org/ xmlhttprequest 規范
https://xhr.spec.whatwg.org/#the-responsetype-attribute responsetype返回類型支持
http://www.zhangxinxu.com/wordpress/2013/10/understand-domstring-document-formdata-blob-file-arraybuffer/ xmlhttprequest 相關學習。
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html xmlhttprequest 使用指南