由于现在身份比较敏感,所以很多情况下你在网购时都会涉及到需要实名认证,此时就需要上传身份证照片。为了让大家能够在任何情况下都可以进行实名认证,前端的上传工作必不可少,如何进行图片上传的功能呢?下面会一步一步的深入到上传的海洋中,让你畅游其中。 

京东推出了整站京东户簿组件,来统一管理用户的实名认证信息。此组件支持不同业务线跨域接入并兼容到 IE7+ 的不同场景。(黑科技)上传的图片支持 OCR 图像识别,可以与输入的证件号码做匹配,防止随便录入信息。

户簿系统组件的界面如下图:

TimLine图片20170814105758

接下来我们来逐步深入的剖析一下此组件的是如何来实现的。上传主要使用了 Ajax 与 XMLHttpRequest(文章中简称为 XHR ) 技术来实现。Ajax 介绍以及背景请参考Ajax 背景

(如不了解  XHR ,可参考 w3.org 的一些文章基础教程文章即可快速了解,可参考如下连接地址:https://www.w3.org/TR/XMLHttpRequest/.)

上传图片功能实现

代码实现:

以上代码中,文件需要通过 FormData 来承载并通过 xhr 发送给服务端,FormData 会有哪些隐患呢?来看一下 XHR 的发展历程。 

XHR 一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了 XHR 标准。XHR 标准又分为Level 1 和 Level 2。

XHR Level 1 中,XHR 有以下三个缺点:

  • 只支持文本数据的传送,无法用来读取和上传二进制文件。
  • 传送和接收数据时,没有进度信息,只能提示有没有完成。
  • 受到“同域限制”(Same Origin Policy),只能向同一域名的服务器请求数据。

XHR Level 2 新版本针对老版本做出了大幅改进:

  • 可以设置 http 请求的时限。
  • 可以使用 FormData 对象管理表单数据。
  • 可以上传文件。
  • 可以请求不同域名下的数据(跨域请求)。
  • 可以获取服务器端的二进制数据。
  • 可以获得数据传输的进度信息。

FormData 参数为 XHR Level 2  中新增接口,所以会存在或多或少的兼容性隐患问题,兼容下如下图:

11

(数据来源: caniuse.com)

根据以上的兼容性,基本上 IE10 以下的浏览器就不用考虑了。

进度条实现

在 XHR 对象中有一个存在这么一个对象:upload。(来个传送门:https://xhr.spec.whatwg.org/#the-upload-attribute

upload是一个XMLHttpRequestUpload,可以将代码优化成以下方法:

跨域功能实现

针对以上需求,我们可以提供2种跨域解决方案: JSONP 与 CORS。

JSONP:实现原理参考:https://en.wikipedia.org/wiki/JSONP。由于 JSONP 无法使用 post 请求,故 PASS。

CORS :全程“跨域资源共享”(Cross-origin Resource Sharing),也是一个 Level2 中新增加的功能。兼容性可参考 caniuse 中 Level2兼容性. 以下提供一张截图

22

所以只能采用此种方式来支持跨域 post 请求的图片上传功能。

CORS 的配置需要前端与后端共同来完成,服务器端需要配置可以访问接入的域。发送请求的时候也会在 http 的响应头中添加  Access-Control-Allow-Origin:*。

* 表明,该资源可以被任意外域访问。如果服务端仅允许来自 http://foo.example 的访问,该首部字段的内容如下:Access-Control-Allow-Origin: http://foo.example

低版本浏览器支持

身为前端开发人员,我相信大家此时都在诅咒微软为啥还不倒闭,微软已经严重的阻碍的大前端的发展脚步,但是抱怨归抱怨,用户群体还是很大的,只能想法来解决了。

根据以上的步骤其实 IE10+ 浏览器已经没问题了,但是因为使用了FormData,CORS 等一些 Level2 的新标准,所以无法再低版本浏览器更好的运行。

解决方案:使用 Flash 来支持低版本浏览器。

市面上比较好的插件有 webUploader ,可以根据浏览器类型来切换 flash 跟 XHR ,单因为此插件需要依赖于 jquery1.10+ 才可以,故无法使用。

最后使用了 SWFUploader 来支持,注:Flash 使用也是存在跨域问题的。

Flash文件的使用方式:

  1. swf放在自己项目中,创建一个 XML,将文件放在与接口同域的服务器上,Crossdomain.xml 文件内容如下,(以下例子为允许所有网站访问)

<?xml version=”1.0” encoding=”UTF-8”?>

    <cross-domain-policy>

    <allow-access-from domain=”*”/>

</cross-domain-policy>

  1. 将 SWF 文件放在与接口同域的服务器上,使用对应 JS 引用 SWF 即可。

以上两种方式均可, SWFUploader 配置调用方式

函数配置好就行了,具体使用方式可以看一下文档:http://www.leeon.me/upload/other/swfupload.html

配置好以后,问题出现:发现在高版本浏览器是没问题的,但是在低版本浏览器是获取不到返回值的。

低版本IE;

33

高版本:

44

接受参数是不同的,所以会出现刚刚的问题,服务端需要按照这个类型来做兼容支持。这样既可实现 IE7+ 的图片上传功能。

增加登录验证支持

看似简单而合理的需求,实则坑道无限。问题,无论低版本或者高版本都是无法上传成功,都会被登录拦截器拦截。

原因:

  1. XHR 在 CORS 时,浏览器认为请求是不安全的,所以不会自动携带 cookie 信息。
  2. Flash 的请求不是在页面发送的,所以也无法携带 cookie 信息。

解决方法:
高版本浏览器:

需要前端后端一直修改来支持此需求,前端改动:

需要给 XHR 的 withCredentials 设置为 true ,并且服务器端也需要打开开关配置才可以正常的传递 cookie。文档描述:https://xhr.spec.whatwg.org/#the-withcredentials-attribute

11

代码如下:

注意:当设置 withCredentials 时,Access-Control-Allow-Origin 不能设置为 * 。必须设置为具体的域名。

IE 浏览器

Flash 无法获取参数,但是可以通过 JS 来获取到 cookie ,并且将 cookie 作为 param 跟随请求一起发送。可以自己手动写,也可以使用插件,swfupload.cookies.js 。此插件是自动添加 cookie 的,只需要引入即可。

解决了以上问题之后,又出现了另外一个问题:cookie 成功传递过去了,依旧无法通过登录过滤器。

经过排查发现,在XHR上传图片的时候都会发送2次请求:如下图

22

来仔细对比一下有什么区别?

第一个请求:

33

第二次请求:

44

 

仔细看发现多了一次类型为 OPTIONS 的请求,而且重要的是不携带 cookie。造成此类请求的原因:当使用 CORS 方式跨域请求时,如果信息为“复杂请求”,那么浏览器会自动发送一条“预检请求”,以获知服务器是否允许该实际请求。“预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响,那么什么样的请求为复杂请求呢?

当请求满足下述任一条件时,则会提前发送预检请求

一、用了下面任一 http 方法:

二、人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

三、Content-Type 的值不属于下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

以下为一个复杂请求的请求过程:

11

由于上传图片属于复杂请求,所以在请求前,浏览器会先发送一次 OPTIONS 请求,服务端需要处理 OPTIONS 类型的请求,以避免被过滤器拦截掉。

到此为止,一个支持高低版本浏览器,以及支持跨域请求的上传图片组件即将开发完成。

封装功能,形成组件

考虑到位了让不同业务线使用,我们需要暴露不同的接口已支持不同的需求:

  • 位于整个dom中的位置,(组件插入的容器选择器)
  • 样式自定义(组件最外层设定的ID)
  • 类型可默认(组件中身份类型的默认值)
  • 操作错误可自定义弹出样式(可以重写弹出错误的函数)
  • 上传图片的服务器url(方便后续维护)
  • 操作错误有回调(方便业务线对不同错误作出相应)
  • 提交保存回调,可让用户独立控制提交(可以让业务线获取保存的结果值,并可灵活的操作提交时机)。

最后来看一下如何来调用:

总结:

通过此组件开发,可以深刻理解跨域的一些常用的解决方案,CORS的用法以及遇到问题时的解决方案。最后希望此篇博客对于正在开发上传功能的同学有一定的帮助。


参考资料列表:

XMLHttpRequest:https://xhr.spec.whatwg.org/ 以及  https://segmentfault.com/a/1190000004322487

XMLHttpRequest Level1与Level2区别介绍:http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html

CORS以及OPTIONS:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

JSONP文档:https://en.wikipedia.org/wiki/JSONP

喜欢(19) 评论(0) 分享

Leave a Reply

© 2014 JDC. All Rights Reserved.