如何替代即將淘汰的Flash方案?
歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~
本文由MarsBoy發表於雲+社群專欄
| 導語 Web技術飛速發展的如今,我們在感受新技術帶來的便捷和喜悅的同時,也時常在考慮著一個問題:老技術如何遷移。正如本文的主題一樣,Flash技術在早年風靡在Web領域,曾經發揮著無盡力量的一個工具正逐漸失去了其重要性。由於效能,相容性,版權問題,Flash的市場正在消退,曾經靠Flash實現的功能和特性如何完美得進行遷移呢,本文將簡單談一談Flash的幾個常見的特性的替代方案。
1.視訊播放(Play Video)
我們知道Flash可以播放.swf檔案的動畫視訊,而且具有很強的控制功能,以前很多Web視訊播放器都是基於Flash去實現的。包括embed標籤,都是如此。所有視訊源為swf的檔案的視訊都需要藉助Flash去播放。
解決方案:
在移動端裝置上,使用HTML5的video標籤基本沒有問題。 在PC上,IE低版本(IE8-)瀏覽器上除了Flash目前沒有其它辦法 在PC上,IE9+和其它現在瀏覽器,採用HTML5標籤。 綜合來說,可以統一用以下一段程式碼實現相容:
<video width="400" height="300" controld> <!--mp4格式適用於IE9+,Chrome,Safari --> <source src="test.mp4" type="video/mp4"></source> <!--ogg格式適用於FireFox,Opera,Chrome --> <source src="test.ogg" type="video/ogg"></source> <!--webm格式適用於FireFox,Opera,Chrome --> <source src="test.webm" type="video/webm"></source> <!--object需要Flash支援,當IE8-時考慮 --> <object data="test.mp4" width="400" height="300"> <!--embed需要Flash支援,當IE8-時考慮 --> <embed src="test.swf" width="400" height="300"> </object> </video>
2.跨域請求(Corss Origin Request)
2.1使用Flash進行跨域請求的方案實現
目前在PC端 ofollow,noindex"> http:// a.qq.com 的頁面請求 http:// b.qq.com 的一個介面是理論上跨域的一個請求,舊版本瀏覽器特別是只支援XMLHTTPRequest Level1的瀏覽器,需要訪問跨域請求,要麼使用jsonp,要麼只能使用Flash。 使用Flash進行跨域需要做的事情是
http:// 1.a.qq.com 的js與Flash互動 2.Flash校驗安全性,檢查 http:// b.qq.com 下根目錄的crossDomain.xml檔案的控制訪問屬性 3.Flash作為中間代理請求 http:// b.qq.com 4.Flash將請求結果返回給 http:// a.qq.com 的js 圖1簡明扼要的描述了這個過程。

圖1 Flash跨域請求
2.2 去Flash跨域如何實現
情況一:CORS(Cross-Origin Resource Sharing)【後端需改造】
條件:要使用CORS,必須在支援 XmlHttpRequest Level2 的瀏覽器中(IE10+和其它現代瀏覽器) 做法:設定withCredentials頭,然後結合後臺設定的Access-Control-Allow-Origin頭進行控制,進行跨域即可。相關程式碼如下: 前端JS:
$.ajax({ url:"http://b.qq.com/api/xxx.php", type:"POST", xhrFields:{ withCredentials:true }, success:function(){ //... }, fail:function(){ //... } })
後端PHP:
<?php //b.qq.com的介面中新增Access-Control-Allow-Origin頭 header("Access-Control-Allow-Origin:http://a.qq.com");
情況二:中轉代理請求【建議】
我們回到同源策略,如果要請求 http:// b.qq.com 下的一個介面,我們從 http:// b.qq.com 下的頁面發起請求,是遵循同源策略的。那麼我們可以在介面域名下放一個統一的html檔案,用於代理我們請求 http:// b.qq.com 的介面,然後將結果告訴 http:// a.qq.com 就可以了。 這種情況下要解決2個主要問題: 1.cookie如何傳送 http:// 2.a.qq.com 與 http:// b.qq.com 的代理頁面前端通訊 其實兩個問題是一個問題, http:// a.qq.com 下的cookie我們是可以獲取到的,同樣的cookie我們可以種在 http:// b.qq.com 下的。問題歸結到第二個問題,如何在前端實現 http:// a.qq.com 和 http:// b.qq.com 兩個頁面之間的通訊。 有兩個方法:
1.使用HTML5規範的PostMessage特性
主要核心邏輯程式碼可以參考: 【 http:// a.qq.com 頁面程式碼】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script> //a.qq.com中邏輯: var $proxyFrame=$("<iframe style='display:none' src='http://b.qq.com/proxy.html'></iframe>").appendTo(document.body); //等待iframe中轉頁面load完畢 $proxyFrame.on("load",function(){ //調中轉頁面 $proxyFrame.get(0).contentWindow.postMessage({ api:"/xx/y", data:{ a:1, b:2 }, cookie:document.cookie//帶過去的cookie }); //回撥 $(window).on("message",function(e){ var event=e.originalEvent; if(event.origin=="http://b.qq.com"){ console.log("response data:",event.data); } }) }) </script> </body> </html>
【http://b.qq.com頁面程式碼】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script> //b.qq.com中邏輯: $(window).on("message",function(e){ var event=e.originalEvent; if(event.origin=="http://a.qq.com"){ var api=event.data.api; var data=event.data.data; var cookie=event.data.cookie; //種植cookie //.........種植cookie的操作 //代理請求介面 $.ajax({ url:api, data:data, //....... success:function(result){ //將response返回給a.qq.com window.parent.postMessage(result,"*") }, fail:function(){ } }) } }) </script> </body> </html>
以上demo簡單解決了前端跨域通訊,跨域帶cookie等問題,在邏輯上完全可以實現跨域通訊。但是對於不支援PostMessage特性的老版瀏覽器是行不通的。比如IE8-瀏覽器就不能很好的支援PostMessage特性。這種情況下我們採用另外一種中轉跨域的方案:降子域通訊。 下面介紹第二種方法:降子域通訊:
2.不支援PostMessage時,降子域通訊
由於 http:// a.qq.com 和 http:// b.qq.com 都是屬於 http:// qq.com 下的子域,同源策略在前端頁面中判定依據是document.domain而不是location.host。而document.domain可寫,可以人為更改到其父域名。這樣 http:// a.qq.com 和 http:// b.qq.com 的兩個頁面都可以自行降到 http:// qq.com 。這樣就可以直接進行通訊。 主要核心邏輯程式碼可以參考: 【 http:// a.qq.com 頁面程式碼】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script> //a.qq.com中邏輯: document.domain="qq.com"; var $crossFrame=$("<iframe style='display:none' src='http://b.qq.com/proxy.html'></iframe>").appendTo(document.body); //等待iframe中轉頁面load完畢 $crossFrame.on("load",function(){ //回撥 window['callback']=function(result){ //收到響應 console.log("receive response:",result); } //調中轉頁面中的方法直接請求 $crossFrame.get(0).contentWindow.request({ api:"/xx/y", data:{ a:1, b:2 } }); }) </script> </body> </html>
【http://b.qq.com頁面程式碼】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script> //b.qq.com中邏輯: document.domain="qq.com"; window.request=function(api,data){ $.ajax({ url:api, data:data, //....... success:function(result){ //將response返回給a.qq.com window.parent.callback(result,"*") }, fail:function(){ } }) } </script> </body> </html>
在實際改造過程中,如果後端結果過多,或者改造不方便,可以直接採用第二種方式——中轉代理的方式進行改造。其原理示意圖總結如下:

圖2 去Flash跨域請求改造指導圖
3.檔案上傳
3.1 背景
其實檔案上傳是HTML規範內的,理論上不需要使用Flash去做。但是隨著ajax技術的興起,Web 2.0時代的到來,input表單的提交改成ajax提交,頁面無重新整理的形式。但是這種形式下對於檔案這類二進位制檔案無法提交,IE下本來有ActiveX 的FSO可以操作,但是外掛的執行需要IE安全機制允許,很多情況下使用者體驗不好,而且相容性也不是很好。於是這種背景下,FLash又擔當起了一個新的功能:檔案上傳。 著名的jquery外掛,ajaxupload.js就是用的Flash進行檔案提交。
3.2去Flash上傳
如何不使用Flash,上傳檔案,而且保證頁面不重新整理,是我們在去Flash上傳工作中需要做的核心。下面針對不同的瀏覽器提供兩套方案:
3.2.1 【第一套方案】HTML5獲取檔案資訊用FormData提交
條件:支援HTML5 FileReader 和FormData 特性 做法:
1.獲取input表單的files物件 2.例項化FileReader物件,並解析files物件 3.解析之後輸出base64編碼的檔案資料 4.base64的資料傳入FormData 5.ajax提交FormData
參考demo如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <input type="file" name="test" id="test" /> <script> $("#test").change(function(e){ var files=e.target.files; va fr=new FileReader(); fr.onload=function(e){ var fm=new FormData(); fm.append("file_test",e.target.result); //額外引數 fm.append("sExtend","test"); //提交ajax $.ajax({ url:'http://b.qq.com/cgi/', type:"POST", dataType:json, data:fm, processData: false, // 不會將 data 引數序列化字串 contentType: false, // 根據表單 input 提交的資料使用其預設的 contentType success:function(result){ console.log(result); }, fail:function(){ console.log("failed"); } }); } fr.readAsDataURL(files[0]); }); </script> </body> </html>
3.2.2 【第二套方案】低版本瀏覽器中用模擬表單提交
條件:無任何條件,支援任何瀏覽器 做法:
1.在頁面上構建一個隱藏的iframe 2.在頁面上構建一個form表單,表單中包含檔案表單和其它附加欄位表單,target設為上述iframe的id 3.上傳檔案動作觸發時,呼叫form的submit方法 4.iframe中載入上傳cgi,返回結果與父視窗通訊,如果iframe與cgi跨域,則參考【第二部分:跨域請求】進行處理
參考demo如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>DEMO-上傳檔案</title> </head> <body> <!-- 以a.qq.com上傳到b.qq.com/upload/為例 --> <form action="http://b.qq.com/upload/" enctype="multipart/form-data" method="post" target="postframe" name="fileform"> <!-- 檔案上傳按鈕 --> <input type="file" name="file_1" /> <!-- 隱藏的附加欄位 --> <input type="hidden" name="sExtend1" value="test1" /> <input type="hidden" name="sExtend2" value="test2" /> </form> <iframe src="" frameborder="0" style="display:none;" id="postframe"></iframe> <script> //監聽檔案基本資訊 $("[name=file_1]").change(function(e){ var files=e.target.files; if("undefined" == typeof files && e.target.value){ //IE9- files=[]; try{ files=[new ActiveXObject("Scripting.FileSystemObject").GetFile(e.target.value)]; }catch(err){ files=[{ name:e.target.value, type:"unkown" }]; } if(!files.length){ files=[{ name:e.target.value, type:"unkown" }]; } } //獲取檔案資訊 console.log(files); }) //上傳 $("[name=fileform]").submit(); //回撥 window.fileCallback=function(result){ //處理result console.log("檔案上傳成功"); } </script> </body> </html>
總結
本文給出了筆者在實際工作中遇到的最常見的去Flash改造的三種場景,現以表格的形式簡單概括如下:
現代H5早期低版本IE等 視訊播放使用H5的video標籤沒辦法只能使用FLash,如果不用Flash,建議提醒使用者升級瀏覽器跨域提交請求使用CORS,前後端結合中轉代理(PostMessage或者降域)Ajax檔案上傳使用FileReader+FormData封裝模擬表單提交到iframe
結語
去Flash不僅是對實現方案的一種相容改造,更是對早已成熟的新技術新思路的運用。目前而言,不管是因為政策原因,還是因為效能或者其它相容性原因,去Flash改造都是重要和緊迫的,本文是筆者在實際工作過程中總結出的最常見的三種去Flash場景和改造方案,供參考,不足之處還請不吝指正。
相關閱讀 再論 ASP.NET 中獲取客戶端IP地址 從零開始的Spring Session 【每日課程推薦】機器學習實戰!快速入門線上廣告業務及CTR相應知識
此文已由作者授權騰訊雲+社群釋出,更多原文請點選
搜尋關注公眾號「雲加社群」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社群!