由于现在身份比较敏感,所以很多情况下你在网购时都会涉及到需要实名认证,此时就需要上传身份证照片。为了让大家能够在任何情况下都可以进行实名认证,前端的上传工作必不可少,如何进行图片上传的功能呢?下面会一步一步的深入到上传的海洋中,让你畅游其中。
京东推出了整站京东户簿组件,来统一管理用户的实名认证信息。此组件支持不同业务线跨域接入并兼容到 IE7+ 的不同场景。
(黑科技)
上传的图片支持 OCR 图像识别,可以与输入的证件号码做匹配,防止随便录入信息。
户簿系统组件的界面如下图:
接下来我们来逐步深入的剖析一下此组件的是如何来实现的。上传主要使用了 Ajax 与 XMLHttpRequest(文章中简称为 XHR ) 技术来实现。Ajax 介绍以及背景请参考
Ajax 背景
。
(如不了解 XHR ,可参考 w3.org 的一些文章基础教程文章即可快速了解,可参考如下连接地址:https://www.w3.org/TR/XMLHttpRequest/.)
上传图片功能实现
代码实现:
1
2
3
4
5
6
7
8
|
var
param
=
new
FormData
();
param
.
append
(
'imgData'
,
file
);
xhr
=
new
XMLHttpRequest
();
xhr
.
addEventListener
(
"load"
,
_self
.
uploadComplete
,
false
);
xhr
.
addEventListener
(
"error"
,
_self
.
uploadFailed
,
false
);
xhr
.
addEventListener
(
"abort"
,
_self
.
uploadCanceled
,
false
);
xhr
.
open
(
"POST"
,
url
);
xhr
.
send
(
param
);
|
以上代码中,文件需要通过
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 中新增接口,所以会存在或多或少的兼容性隐患问题,兼容下如下图:
(数据来源: caniuse.com)
根据以上的兼容性,基本上 IE10 以下的浏览器就不用考虑了。
进度条实现
在 XHR 对象中有一个存在这么一个对象:upload。(来个传送门:
https://xhr.spec.whatwg.org/#the-upload-attribute
)
upload是一个XMLHttpRequestUpload,可以将代码优化成以下方法:
1
2
3
4
5
6
7
8
9
10
|
var
param
=
new
FormData
();
param
.
append
(
'imgData'
,
file
);
xhr
=
new
XMLHttpRequest
();
/*新增进度条监听函数*/
xhr
.
upload
.
addEventListener
(
"progress"
,
_self
.
uploadProgress
,
false
);
xhr
.
addEventListener
(
"load"
,
_self
.
uploadComplete
,
false
);
xhr
.
addEventListener
(
"error"
,
_self
.
uploadFailed
,
false
);
xhr
.
addEventListener
(
"abort"
,
_self
.
uploadCanceled
,
false
);
xhr
.
open
(
"POST"
,
url
);
xhr
.
send
(
param
);
|
跨域功能实现
针对以上需求,我们可以提供2种跨域解决方案: JSONP 与 CORS。
JSONP:实现原理参考:
https://en.wikipedia.org/wiki/JSONP
。由于 JSONP 无法使用 post 请求,故 PASS。
CORS :全程“跨域资源共享”(Cross-origin Resource Sharing),也是一个 Level2 中新增加的功能。兼容性可参考 caniuse 中
Level2兼容性
. 以下提供一张截图
所以只能采用此种方式来支持跨域 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文件的使用方式:
-
swf放在自己项目中,创建一个 XML,将文件放在与接口同域的服务器上,Crossdomain.xml 文件内容如下,(以下例子为允许所有网站访问)
-
将 SWF 文件放在与接口同域的服务器上,使用对应 JS 引用 SWF 即可。
以上两种方式均可, SWFUploader 配置调用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0
31
32
33
34
35
36
37
38
39
40
41
42
43
|
var
setting
=
{
debug
:
false
,
upload_url
:
url
,
//服务器接受文件路径
flash_url
:
'flashurl/swfupload.swf?ver='
+
Math
.
random
(),
//上传的swf文件路径
//文件配置
file_post_name
:
'imgData'
,
//服务器接受的文件数据key值
post_params
:
{
sessionId
:
opts
.
param
.
sessionId
},
//上传文件时带的参数
file_types
:
'*.bmp;*.jpeg;*.jpg;*.gif;*.png'
,
//文件类型过滤配置
file_types_description
:
'浏览器图片格式'
,
//上传文件类型描述
file_size_limit
:
opts
.
fileSizeLimit
,
//指定要上传的文件的最大体积,
//可以带单位,合法的单位有:B、KB、MB、GB,如果省略了单位,则默认为KB。该属性为0时,表示不限制文件的大小。
file_upload_limit
:
"200"
,
file_queue_limit
:
"1"
,
prevent_swf_caching
:
false
,
preserve_relative_urls
:
false
,
//按钮配置
button_placeholder_id
:
opts
.
id
,
button_width
:
(
opts
.
type
==
'pop'
)
?
160
:
175
,
button_height
:
(
opts
.
type
==
'pop'
)
?
100
:
109
,
button_text
:
"
"
,
button_text_style
:
""
,
button_text_left_padding
:
0
,
button_text_top_padding
:
0
,
button_action
:
SWFUpload
.
BUTTON_ACTION
.
SELECT_FILES
,
button_disabled
:
false
,
button_cursor
:
SWFUpload
.
CURSOR
.
HAND
,
button_window_mode
:
"transparent"
,
custom_settings
:
{
index
:
index
},
// 事件
file_dialog_start_handler
:
_self
.
fileDialogStart
,
file_queued_handler
:
_self
.
fileQueued
,
file_queue_error_handler
:
_self
.
fileQueueError
,
file_dialog_complete_handler
:
_self
.
fileDialogComplete
,
upload_progress_handler
:
_self
.
uploadProgress
,
upload_error_handler
:
_self
.
uploadError
,
upload_success_handler
:
_self
.
uploadSuccess
,
upload_complete_handler
:
_self
.
uploadComplete
}
var
swfupload
=
new
SWFUpload
(
setting
)
|
函数配置好就行了,具体使用方式可以看一下文档:
http://www.leeon.me/upload/other/swfupload.html
配置好以后,问题出现:发现在高版本浏览器是没问题的,但是在低版本浏览器是获取不到返回值的。
低版本IE;
高版本:
接受参数是不同的,所以会出现刚刚的问题,服务端需要按照这个类型来做兼容支持。这样既可实现 IE7+ 的图片上传功能。
增加登录验证支持
看似简单而合理的需求,实则坑道无限。问题,无论低版本或者高版本都是无法上传成功,都会被登录拦截器拦截。
原因:
-
XHR 在 CORS 时,浏览器认为请求是不安全的,所以不会自动携带 cookie 信息。
-
Flash 的请求不是在页面发送的,所以也无法携带 cookie 信息。
解决方法:
高版本浏览器:
需要前端后端一直修改来支持此需求,前端改动:
需要给 XHR 的 withCredentials 设置为 true ,并且服务器端也需要打开开关配置才可以正常的传递 cookie。文档描述:
https://xhr.spec.whatwg.org/#the-withcredentials-attribute
代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
var
param
=
new
FormData
();
param
.
append
(
'imgData'
,
file
);
xhr
=
new
XMLHttpRequest
();
/*新增进度条监听函数*/
xhr
.
upload
.
addEventListener
(
"progress"
,
_self
.
uploadProgress
,
false
);
xhr
.
addEventListener
(
"load"
,
_self
.
uploadComplete
,
false
);
xhr
.
addEventListener
(
"error"
,
_self
.
uploadFailed
,
false
);
xhr
.
addEventListener
(
"abort"
,
_self
.
uploadCanceled
,
false
);
xhr
.
open
|