本文由 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
- });
- // 回调
- $(Windows).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 中逻辑:
- $(Windows).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
- Windows.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() {
- // 回调
- Windows['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";
- Windows.request = function(API, data) {
- $.Ajax({
- url: API,
- data: data,
- //.......
- success: function(result) {
- // 将 response 返回给 a.qq.com
- Windows.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();
- // 回调
- Windows.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 场景和改造方案, 供参考, 不足之处还请不吝指正.
来源: https://www.cnblogs.com/qcloud1001/p/9803086.html