最近M端项目中涉及到图片上传功能,现把项目中遇到的一些问题及解决办法分享如下,与各位共同探讨:
一、相关需求:
1. 客户端上限10M
2. 服务器端上限2M
3. 文件过滤
4. 显示上传进度
5. 异步上传
6. 多文件上传
二、需求分析:
1. 实现2,可使用canvas在前端实现压缩(base64);
2. 实1、3,可采用file.size及/image/.test(file.type)过滤;
3. 实现4,使用XHR2实现上传,添加进度时间监控,xhr.upload.addEventListener(“progress”, uploadProgress, false);
4. 实现5,使用XHR2实现上传;
5. 多文件上传,单文件循环上传即可,但是兼容进度时,需单文件各自监控;
三、需求实现:
实现一:二进制方式上传
需求1、3、4、5、6皆可实现,但是服务器端上传2M,使用canvas方式压缩后生成的是base64,若使用此方式上传,必须把base64转换成二进制流,GitHub上也有相关文章把base64转换成二进制流的,使用xhr.sendAsBinary()发送二进制流,参考此文,测了一部分常见机型,可以实现,具体是否可以在项目中使用还有待论证。二进制上传实现部分代码仅供参考:
01 |
var uploadFile = function (fileid, file) { |
02 |
var xhr = new XMLHttpRequest(), |
04 |
url = "/picture/upload" , |
06 |
uploadProgress = function (evt) { |
07 |
if (evt.lengthComputable) { |
08 |
var percent = (evt.loaded * 100 / evt.total).toFixed(1); |
11 |
console.log( 'unable to compute' ); |
14 |
uploadFailed = function (evt) { |
17 |
uploadCanceled = function (evt) { |
21 |
xhr.upload.addEventListener( "progress" , uploadProgress, false ); |
22 |
xhr.addEventListener( "error" , uploadFailed, false ); |
23 |
xhr.addEventListener( "abort" , uploadCanceled, false ); |
24 |
xhr.open( "POST" , url , true ); |
25 |
xhr.onreadystatechange = function (e) { |
26 |
if (xhr.readyState == 4) { |
27 |
if (xhr.status === 200) { |
28 |
var data = xhr.response; |
40 |
fd.append(fileid, file); |
实现二:base64上传
需求1、2、3、5、6皆可实现,实现此种方式即基本的Get上传,但是无法实时监控上传文件进度,需求4无法实现。
实现三:二进制+base64
即上述两种方案的综合。也可参考此文移动端Web上传图片实践中的实例。
四、问题总结:
M端浏览器各异,支持情况各异,现总结如下:
a) 部分酷派机型浏览器(微信、UC、QQ、百度),中兴自带浏览器不支持input[type=file];
解决方式:放弃
b) Adroid机型,不同浏览器对input[type=file]支持不同,有的没有图库选项,有的没有相机选项。主要表现为小米、酷派部分机型的微信自带浏览器。
解决方式:input[type=file]添加accept=’image/*’属性,可实现某些adriod机型不出现文档选项。
c) 上传文件时,出现图片自动旋转的问题
解决方式:实现开源插件CanvasResize中exif.js来纠正,实现此插件可解决压缩、纠正图片旋转,但Adroid上UC浏览器中会出现下图问题:(国外人写的插件哪会管国内浏览器死活!)
最后采用的腾讯的一款压缩方案,解决了UC浏览器的问题。
d) 使用压缩插件时需注意,PNG图片压缩时往往会偏大,可把压缩成image/jpeg格式;
var cvs = document.createElement(‘canvas’);
var ctx = cvs.getContext(“2d”).drawImage(source_img_obj, 0, 0);
var newImageData = cvs.toDataURL(‘image/jpeg’, quality/100);
f) 因浏览器对input[type=file]显示风格各异,项目使用label的for指向input[type=file]的id,并设置input{display:none};在Adroid部分浏览器上点击无反应;
解决方式:设置input{position: absolute; top: -999em;}来隐藏。
#20150923 update start
使用label标签for属性触发input[type=file]需要注意的一些问题:
1. label的for指向的是input[text/radio/checkbox/date]等元素trigger的是focus事件,而指向input[file]元素trigger的是click 事件,因此可以打开一个浏览器窗口,这就是我们使用label的for指向input[file],使用zepto的tap事件绑定时,不会触发的原因。
2. 给input[type=file]设置display: none or visibility: hidden将不会工作,因为表单提交时input的值不会被发送到服务端。除上面解决的top:-999em;也可以使用以下CSS来设置隐藏:
你可能会有疑问,这里为什么不把宽高设置为0而为0.1px。设置为0在一些浏览器tab页可能不会被解析。同时设置position:absolute属性来使其脱离标准流,不会影响其他元素的布局。
如是想,使用label标签的for属性,我们就可以使用CSS3各种属性来DIY按钮各种样式及效果了,不是吗?
同时可以设置当input[file]focus时,label的outline样式:
1 |
.input[type=file]:focus + label { |
2 |
outline : 1px dotted #000 ; |
3 |
outline : -webkit-focus-ring-color auto 5px ; |
Firefox中对:hover,:active支持良好,当时会忽略input[type=”file”]:focus设置,但是Firefox对input[file]支持focus、blur事件,我们可以通过JS来增加和删除类has-focus来实现:
1 |
input.addEventListener( 'focus' , function (){ input.classList.add( 'has-focus' ); }); |
2 |
input.addEventListener( 'blur' , function (){ input.classList.remove( 'has-focus' ); }); |
3 |
.inputfile:focus + label, |
4 |
.inputfile.has-focus + label { |
5 |
outline: 1px dotted #000; |
6 |
outline: -webkit-focus-ring-color auto 5px; |
3. input[file]多选(multiple),现在多选在移动端和PC端支持都不太好,PC端IE9及以下浏览器不支持该属性;移动端andriod系统某些自带浏览器、UC浏览器支持情况各异。
#20150923 update end
g) 在部分Adroid支持input[type=file]的浏览器中,当使用/image/.test(file.type)时,选择图片文件会返回false。使用JSON.stringify(file)分析后发现,是file对象中的name字段中没有包含后缀,同时type字段为空,使用this.value获取路径中也没有包含后缀。因此过滤出现问题。
如下结果:
1 |
{ "webkitRelativePath" : "" , "lastModified" :1433304214000, "lastModifiedDate" : "2015-06-03T04:03:34.000Z" , |
2 |
"name" : "fanmian" , "type" : "" , "size" :2273852} |
正常结果如下:
1 |
{ "webkitRelativePath" : "" , "lastModified" :1433304214000, "lastModifiedDate" : "2015-06-03T04:03:34.000Z" , |
2 |
"name" : "fanmian.png" , "type" : "image/png" , "size" :2273852} |
解决方式:放开/image/.test(file.type)过滤,在压缩时,抛出错误过滤。
h) html5上传文件,Firefox支持重复选择同一文件,其它浏览器不支持
解决方式:每次选择文件后给input[type=file]赋值空。
2015-09-09补充
input[type=file]控件比较特殊:
对于ios,已实现file,ios7版本之前,可以唤起照片集里的图片文件;ios7后,实现了拍照和录像的功能。不过在7.0.3里有bug,程序会闪退;
对于andriod,如果使用的是浏览器,file类型的文件选择,会唤起浏览器实现的文件选择,不过文件的选择,不同的手机,具体实现不同,web无法控制。如果在android app里使用webkit的方式,需要android的webkit实现私有api接口,才能实现file选择上传。
input[type=file]控件在M端浏览器支持情况(部分机型) by qianqian and xiaocui of my team —2015.12.12 add
编号 |
手机设备 |
测试浏览器 |
是否支持拍照 |
是否支持从相册选择 |
1 |
iphone4S |
微信浏览器 |
支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
2 |
iphone5S |
微信浏览器 |
支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
3 |
iphone6 |
微信浏览器 |
支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
4 |
iphone6 plus |
微信浏览器 |
支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
5 |
小米手机2S |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
6 |
小米手机M4 |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
7 |
三星S6 ED |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
8 |
三星SM-G5308W |
微信浏览器 |
不支持 |
不支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
9 |
华为荣耀7 |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
10 |
华为P7 |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
不支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
11 |
华为PE-TL10 |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
支持 |
支持 |
12 |
酷派8675-A |
微信浏览器 |
不支持 |
支持 |
QQ浏览器 |
不支持 |
支持 |
UC浏览器 |
支持 |
支持 |
手机自带浏览器 |
不支持 |
不支持 |
五、参考链接:
Html5 File Upload with Progress
移动端Web上传图片实践
图片压缩成base64,采用二进流上传
Styling & Customizing File Inputs the Smart Way
注:本文为转载文章,原文信息如下:
转载声明:
本文标题:文件上传那些事儿
本文链接:http://www.zuojj.com/archives/1402.html,转载请注明转自Benjamin-专注前端开发和用户体验