一、前言
随着HTML5规范的提出,我们又多了一种上传方式的选择。相对企业信息系统而言,互联网产品的用户群体要广泛很多,不同的用户群体通常会选择不同的浏览器,不同的浏览器对HTML5规范的支持程度不同。单一的上传方式根本无法满足所有用户。我们需要采用渐进式增强的上传方式为用户提供较好的上传体验。
二、案例分析
我们以QQ相册作为案例,分析渐进式增强的上传方式。
先看一下QQ相册的图片预览功能:
是不是很炫?以上界面是用Flash实现的,现在我禁用Flash:
再看看QQ相册,先提示我安装浏览器插件:
我选择不安装,界面就变成这样:
我们看到了丑陋的input type='file'了。底部可以看到提示语:请安装Flash或使用极速上传。
我选择极速上传并安装浏览器插件,再启用Flash插件,重新进入相册上传界面:
界面是不是更炫了?连本地磁盘的图片都被读取了。浏览器插件是不是很可怕?
由此,我们可以看出QQ相册的图片上传功能,渐进式增强的优先级是这样的:
浏览器插件 -> Flash -> 普通上传
整个过程压根儿没考虑HTML5的解决方案。也许跟QQ相册的用户群体有关。
三、139邮箱文件上传渐进式增强
通过对业界其它产品的分析再结合我们自己产品的用户特征,139邮箱文件上传渐进式增强的优先级是这样的:
浏览器插件 -> HTML5 -> Flash -> 普通上传
什么?为什么139邮箱的上传方式HTML5优先于Flash?原因如下:
(1)、HTML5是浏览器原生提供的能力,默认情况下无法被禁用,而且不需要另外加载文件,可减少http请求。
(2)、有些浏览器会默认禁用Flash插件,比如FireFox25默认情况下就是禁用Flash的,需要单击地址栏左侧的红色图标才能启用Flash插件:
(3)、第三个原因也是最重要的原因,公司某些领导偏爱FireFox浏览器又不知道怎么启用Flash插件。领导的爱好决定了产品的走向,是不是很蛋疼?如果你们公司的领导偏爱其它浏览器,请自行调整优先级。
四、能力检测
确定渐进式增强的优先级之后,接下来就需要对用户的浏览器做能力检测:是否安装自定义的浏览器插件、是否支持HTML5上传、是否安装Flash插件。
判断是否安装自定义的浏览器插件,完整代码如下:
function isUploadControlSetup() { var setup = false; if (window.ActiveXObject) {//ie try { if (new ActiveXObject("Cxdndctrl.Upload")) { setup = true; } } catch (ex) { try { if (new ActiveXObject("ExCxdndCtrl.ExUpload")) { setup = true; } } catch (e) { console.log(ex); console.log('创建ActiveXObject("Cxdndctrl.Upload")及ActiveXObject("ExCxdndCtrl.ExUpload")对象失败!'); } } } else if (navigator.plugins) {//firefox chrome var mimetype = navigator.mimeTypes["application/x-richinfo-cxdnd3"]; setup = (mimetype && mimetype.enabledPlugin) ? true : false; } return setup; }
粗体字部分需要替换成创建自己的浏览器插件实例对象所需的字符串参数。如139邮箱小工具参数值为:Cxdndctrl.Upload
判断是否支持HTML5的上传方式,完整代码如下:
function isSupportHtml5Upload() { if (window.File && window.FileList && window.FileReader && window.Blob && window.FormData && window.Worker && "withCredentials" in (new XMLHttpRequest)) { return true; } return false; }
判断是否安装Flash插件,IE浏览器需要通过try{}catch(){}试探创建ActiveXObject实例,标准浏览器可迭代navigator.plugins,完整的代码如下:
function getVersionInIE() { var version = 0; var axo; // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry try { // version will be set for 7.X or greater players axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); version = axo.GetVariable("$version"); } catch (e) { } if (!version) { try { // version will be set for 6.X players only axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); // installed player is some revision of 6.0 // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, // so we have to be careful. // default to the first public version version = "WIN 6,0,21,0"; // throws if AllowScripAccess does not exist (introduced in 6.0r47) axo.AllowScriptAccess = "always"; // safe to call for 6.0r47 or greater version = axo.GetVariable("$version"); } catch (e) { } } if (!version) { try { // version will be set for 4.X or 5.X player axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); version = axo.GetVariable("$version"); } catch (e) { } } if (!version) { try { // version will be set for 3.X player axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); version = "WIN 3,0,18,0"; } catch (e) { } } if (!version) { try { // version will be set for 2.X player axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); version = "WIN 2,0,0,11"; } catch (e) { } } if (version !== 0) { var match = version.match(/(d+),(d+).*$/); if (match[0]) { version = Number(match[1] + "." + match[2]); } else { version = 0; } } return version; } function getVersionInOthers() { var v = 0; if (navigator.plugins && navigator.plugins.length > 0 && navigator.plugins["Shockwave Flash"]) { var plugins = navigator.plugins["Shockwave Flash"]; for (var i = 0; i < plugins.length; i++) { var swf = plugins[i]; if (swf.enabledPlugin && (swf.suffixes.indexOf("swf") != -1) && navigator.mimeTypes["application/x-shockwave-flash"]) { var match = plugins.description.match(/ (d+(?:.d+)?)/); if (match) { var v = parseInt(match[1]); break; } } } } return v; }
五、HTML5上传方式分析
断点续传,秒传都是建立在文件分块的基础之上的,分块上传需要读取文件块的内容,并计算文件内容的MD5值,读取文件内容需要用到FileReader,而计算MD5值需要用到Worker
为什么我们需要使用Worker来计算MD5值?
因为计算MD5值需要消耗较多的CPU时间,浏览器本身是采用单线程模式工作的,为了避免MD5值的计算阻塞其它JS代码的执行,我们需要使用Worker对象创建新线程完成MD5值的计算。
六、什么?你是IE6用户而且还有洁癖不想安装任何浏览器插件,下图红圈按钮就是为你量身定制的,欢乐的戳它吧