案例背景
MBD要增加认证功能,要能够上传图片。
lz想偷懒用一个现成的js库来完成html5下带进度的上传控件,于是各种找。
发现一个这个:resumable.js
比其他几个类似 plupload轻量很多,纯粹只针对html5,完全满足移动端需要。果断采用。
能分块上传!能断点续传!能暂停继续!
好吧,lz很兴奋。
完成前端代码编写以后,就开始各种碰壁了……
第一次碰壁:
我们上传文件的保存用了YunStorage,但是,这个坑爹货上传的每个文件的位置都是随机的。要完成断点续传和分块上传,必须所有碎片文件放在一起。在现有架构下,根本无法保证上传的文件都在一个服务器上。除非做专门服务器设置更改,于是决定放弃这个功能,改为只用基本上传功能。
第二次碰壁:
lz手机是Android4.4版本,pc开发时候用Chrome进行模拟,开发工作一切正常,提交测试时候,测试发现安卓测试机除了Chrome浏览器外基本没有能够成功上传的……反倒是苹果设备一切正常。
查了一下服务器日志,上传的内容对比之下,安卓测试机提交的文件,所有信息都对,唯独,获取的上传文件是0kb!!!
同样的代码同样的环境换lz手机一切OK。排除服务器问题后,怀疑是上传组件的问题。艰难的换成了plupload之后,bug更多,测试机上甚至不能弹出文件选择界面,可能是版本问题,一些函数都无法找到。而且要额外加载的文件由压缩前28kb变成压缩后108kb。
决定换回之前的方案,排查问题所在。
问题排查
监视logcat日志,用测试机连接pc查看日志,在前端脚本中输出log。
排查后发现日志无问题,XMLHttpRequest对象在send前准备的数据正常,上传之前读取文件大小等信息全都正确。无解。
安装抓包工具,抓测试手机发往服务器的数据包,发现lz手机发包有大量payload数据。
上传内容也确实有图片数据
但是,测试机的数据却是这个样子:
直接就结束了!!!什么都没有发送!!!!
pc的Chrome调试下监测数据,FormData里面append了一个blob对象,这个对象就是上传的文件数据。
崩溃,度娘无解,没人问这个问题,谁也不知道。
这时候果断的找谷歌。
我都还没敲完好吧,谷歌就知道了!!!!
然后很多人都在讨论,原来这个是Android系统的一个bug。
XMLHttpRequest在发送blob对象的时候,会发空文件,在4.0-4.1版本确实存在,不过后续没人讨论,据说4.2还是4.3就修复了。
有人提供了一个替代方案,用ArrayBuffer替代Blob传输文件,但是,但是,lz研究了一下发现这个方法好像不能传别的参数。因为直接把文件作为元数据post了。或者可以解决,但是改动成本太高。
尝试将ArrayBuffer数据转为Base64数据作为文本进行传输,结果发现js自定义方法转的base64数据,php接收后尝试解出再还原成文件过程中不知道哪里不对,总之就是无法实现。
然后,lz终于看见有人说了一句:不用Blob对象,用File对象就可以了,只是这样就不能分块了。
分毛块啊,服务器就不能分块了好吧!!!
就是因为分块才需要用Blob的好吧,Blob的slice()方法,将元数据切割形成新的Blob对象。
果断修改resumable.js的代码,增加了一段,如果设置块大小大于或等于文件大小,就不进行分块操作,直接把File对象传递给FormData。
然后,颤颤巍巍的点了一下上传。。。。。
0%
84%
上传成功……
if($.fileObjSize <= $.endByte){ bytes = $.fileObj.file; }
三行代码解决问题,追查bug用了2天……
总结
好吧,告诉一下小伙伴们,Android设备好多有bug,用XMLHttpRequest进行ajax上传文件的时候要知道不能用Blob对象。
我想对安卓的小伙伴们说,贵圈太乱,还是水果好……
参考信息
MDN – Web技术文档 – Web API 接口 – XMLHttpRequest
MDN – Web技术文档 – Web API 接口 – XMLHttpRequest – FormData
MDN – Web技术文档 – Web API 接口 – Blob
MDN – Web技术文档 – Web API 接口 – ArrayBuffer
Issue 39882: Browser: Trying to send a Blob with XHR2 sends the request with an empty body