文件上传那些事儿
今日头条:20150601”东方之星“客轮湖北监利沉船,可能是中国有史以来的最大的沉船事故。
最近 M 端项目中涉及到图片上传功能,现把项目中遇到的一些问题及解决办法分享如下,与各位共同探讨:
一、相关需求:
- 客户端上限 10M
- 服务器端上限 2M
- 文件过滤
- 显示上传进度
- 异步上传
- 多文件上传
二、需求分析:
- 实现 2,可使用 canvas 在前端实现压缩(base64);
- 实现 1、3,可采用 file.size 及/image/.test(file.type)过滤;
- 实现 4,使用 XHR2 实现上传,添加进度时间监控,xhr.upload.addEventListener(“progress”, uploadProgress, false);
- 实现 5,使用 XHR2 实现上传;
- 多文件上传,单文件循环上传即可,但是兼容进度时,需单文件各自监控;
三、需求实现:
实现一:二进制方式上传
需求 1、3、4、5、6 皆可实现,但是服务器端上传 2M,使用 canvas 方式压缩后生成的是 base64,若使用此方式上传,必须把 base64 转换成二进制流,GitHub 上也有相关文章把 base64 转换成二进制流的,使用 xhr.sendAsBinary()发送二进制流,参考此文,测了一部分常见机型,可以实现,具体是否可以在项目中使用还有待论证。二进制上传实现部分代码仅供参考:
1 | var uploadFile = function(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 格式;
1 | var cvs = document.createElement("canvas"); |
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]需要注意的一些问题:
- label 的 for 指向的是 input[text/radio/checkbox/date]等元素 trigger 的是 focus 事件,而指向 input[file]元素 trigger 的是 click 事件,因此可以打开一个浏览器窗口,这就是我们使用 label 的 for 指向 input[file],使用 zepto 的 tap 事件绑定时,不会触发的原因。
- 给 input[type=file]设置 display: none or visibility: hidden 将不会工作,因为表单提交时 input 的值不会被发送到服务端。除上面解决的 top:-999em;也可以使用以下 CSS 来设置隐藏:
input[type=file] { width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1; }你可能会有疑问,这里为什么不把宽高设置为 0 而为 0.1px。设置为 0 在一些浏览器 tab 页可能不会被解析。同时设置 position:absolute 属性来使其脱离标准流,不会影响其他元素的布局。如是想,使用 label 标签的 for 属性,我们就可以使用 CSS3 各种属性来 DIY 按钮各种样式及效果了,不是吗?
同时可以设置当 input[file]focus 时,label 的 outline 样式:
1
2
3
4 .input[type=file]:focus + label {
outline: 1px dotted #000;
outline: -webkit-focus-ring-color auto 5px;
}Firefox 中对:hover,:active 支持良好,当时会忽略 input[type=”file”]:focus 设置,但是 Firefox 对 input[file]支持 focus、blur 事件,我们可以通过 JS 来增加和删除类 has-focus 来实现:
1
2
3
4
5
6
7 input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); });
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); });
.inputfile:focus + label,
.inputfile.has-focus + label {
outline: 1px dotted #000;
outline: -webkit-focus-ring-color auto 5px;
}
- 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", |
1 | {"webkitRelativePath":"","lastModified":1433304214000,"lastModifiedDate":"2015-06-03T04:03:34.000Z", |
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]浏览器兼容列表
input[type=file]控件在M端浏览器支持情况(部分机型) by qianqian and xiaocui of my team updated at 2015.12.12
编号 | 手机设备 | 测试浏览器 | 是否支持拍照 | 是否支持从相册选择 |
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
转载申请
本作品采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,文章内图片请保留全部内容。