如何替代即将淘汰的Flash方案?

47次阅读

共计 7016 个字符,预计需要花费 18 分钟才能阅读完成。

欢迎大家前往腾讯云 + 社区,获取更多腾讯海量技术实践干货哦~
本文由 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 简明扼要的描述了这个过程。
图 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>
在实际改造过程中,如果后端结果过多,或者改造不方便,可以直接采用第二种方式——中转代理的方式进行改造。其原理示意图总结如下:
图 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 相应知识

正文完
 0