异步上传文件实现的浅析
12 Jun 2017
文件上传,是一个挺常见的需求, 一般来说多的是上传图片,或者后台系统做报表上传等操作。
平时项目里用到的上传图片,都是成型的相册上传插件,比较少涉及到源码那块的研究,后来涉及到比较特殊的上传图片业务要求,于是开始使用封装的ajaxUpload
插件, 源码来源url似乎已经访问不了。不过在使用过程中,遇到了一系列问题,于是开始了这篇文章的书写。
关于 ajaxUpload 原理
本身做了兼容ie的处理,由于一般表单提交,是带上页面刷新的,要想做到异步无刷新页面提交,一般表单,可以通过ajax来实现,但涉及到文件上传,ajax的支持覆盖度就没那么广了,于是改为了页面刷新,但刷新的不是业务页面,而是建立一个隐藏的iframe,表单提交可以指定target到iframe,通过监听iframe的加载来解析iframe变化的文档内容,获取提交后的返回信息。这就是ajaxUpload的主要逻辑。
这里有个知识点,说到ajax来实现表单异步提交,那么正常的表单提交submit是同步的吗?带着这个疑问,我们来看一下ajaxupload的源码:
代码说短不短,说长也不长,因为作为插件,业务需要被修改过,未能找到比较完整的源码,不过核心逻辑还是比较清晰的。
画了个简单示意图,即在按钮处覆盖透明的div,实现当点击按钮时,实际触发了input file的点击,当input change事件触发时,又调用了插件的submit方法,生成iframe和form,并提交form到iframe,最后监听iframe load事件处理响应信息。
查看原图
结合代码看,会发现,iframe的onload事件绑定是在form submit之后,如果前面说的表单提交是同步行为的话,那么在这里就会有疑惑了,但这里恰恰好说明了,submit是一个异步行为,通过断点调试也可以发现这一问题,就是当js执行到submit之后,在后续js还没执行结束之前,我们看到network是不会立即发起http请求的(chrome 开发者工具查看),后来在Stack Overflow得到类似的说明。
测试例子代码:
服务我用koa1实现的
以上代码实验结果是,表单提交事件会在click事件回调代码执行完毕之后才发起,console输出结果是:
click->after submit action->submit->timeout quick
小困惑
最近在使用该插件实现上传excel文件时,遇到了一个坑(实际上并非这个插件本身的坑),在实际测试过程中,遇到了读取iframe document时出现跨域报错,通过一系列简单的示例代码,却找不到问题所在,后来发现iframe的document里需要手动设置domain=xxx;
才能正常供父页面访问,于是关注点迁移到了domain
身上,因为使用该插件结合的jquery库,是全站公共库文件,其中添加了domain
的主域设置,查了一遍资料发现,domain
如果手动设置了,那么iframe的页面不管是否跨域,如果想要访问得了必须也显式设置domain
才可以访问(当然非跨子域的那些跨域设置了也没用)。
总结
其实,说来这个实现还算简单,通过form target指向iframe的形式,间接实现无刷新页面异步提交。
当然除了这个比较广泛的实现外,现在新型的实现也早就有的了,具体可以参考 阮一峰的博文《文件上传的渐进式增强》,这里就不描述了。