如何替代即將淘汰的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端a.qq.com的頁面請求b.qq.com的一個接口是理論上跨域的一個請求,舊版本瀏覽器特別是只支持XMLHTTPRequest Level1的瀏覽器,需要訪問跨域請求,要麼使用jsonp,要麼只能使用Flash。 使用Flash進行跨域需要做的事情是

1.a.qq.com的js與Flash交互 2.Flash校驗安全性,檢查b.qq.com下根目錄的crossDomain.xml文件的控制訪問屬性 3.Flash作爲中間代理請求b.qq.com 4.Flash將請求結果返回給a.qq.com的js 圖1簡明扼要的描述了這個過程。

img圖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");

情況二:中轉代理請求【建議】

我們回到同源策略,如果要請求b.qq.com下的一個接口,我們從b.qq.com下的頁面發起請求,是遵循同源策略的。那麼我們可以在接口域名下放一個統一的html文件,用於代理我們請求b.qq.com的接口,然後將結果告訴a.qq.com就可以了。 這種情況下要解決2個主要問題: 1.cookie如何發送 2.a.qq.com與b.qq.com的代理頁面前端通信 其實兩個問題是一個問題,a.qq.com下的cookie我們是可以獲取到的,同樣的cookie我們可以種在b.qq.com下的。問題歸結到第二個問題,如何在前端實現a.qq.com和b.qq.com兩個頁面之間的通信。 有兩個方法:

1.使用HTML5規範的PostMessage特性

主要核心邏輯代碼可以參考: 【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>

【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時,降子域通信

由於a.qq.com和b.qq.com都是屬於qq.com下的子域,同源策略在前端頁面中判定依據是document.domain而不是location.host。而document.domain可寫,可以人爲更改到其父域名。這樣a.qq.com和b.qq.com的兩個頁面都可以自行降到qq.com。這樣就可以直接進行通信。 主要核心邏輯代碼可以參考: 【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>

【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>

在實際改造過程中,如果後端結果過多,或者改造不方便,可以直接採用第二種方式——中轉代理的方式進行改造。其原理示意圖總結如下:

img圖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 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

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