基于iFrame的异步文件上传

产品中要实现一个向导式的表单页面,第一步,用户可以上传图片,且能在不刷新页面的情况下看到所上传图片的预览,第二步,用户填写完成其余表单项,提交表单时该图片会一起被提交。不依赖Flash或HTML5。

一开始的方案是用Javascript来做本地的预览,这一方案依赖于如下JS API:

而这一API在FireFox7以后就不被支持了(兼容方案在这里: http://blog.csdn.net/teresa502/article/details/7241776 ),IE更是悲剧。做兼容代码在紧张的项目计划下显得成本有些高,所以很干脆的放弃了基于纯浏览器端的思路。

基于iFrame这样的老技术,配合服务器端也可以实现这一功能。

表单一:

浏览选中图片后提交这一表单,会自动将图片提交到/upload/image服务,注意提交目标是一个用户不可见的iFrame,所以表单页面不会被刷新。

服务器端代码处理上传文件,为文件生成一个散列的文件名,将文件保存至磁盘,或是保存至缓存中(需注意多用户的情况),成功则返回一段JSON:

注意上述JSON响应如果mimetype设为application/json,IE会很高兴的提示用户下载文件…… 所以改成了text/plain。注:一般而言,返回的纯文本文本内容text/plain比text/html要安全。

下边这段代码监听了iFrame的onload事件,

这里jQuery的contents()方法返回的是包装过的iframe.contentWindow.document,浏览器显示text/plain时是把内容包到自己的HTMLDocument DOM中。不去关心这一DOM结构如何,直接使用find(“*”).first().text()就把文本拿出来了。把JSON里的filename字段赋值给预览的<img />,即可完成预览。注意:这里的代码并没有处理上传失败的各种情况。

表单二:

这个表单额外提供了一个隐藏域,在imageFrame的onload监听方法里个这个字段赋值,即可将上传图片路径随该表单一起提交。

至此基本功能已经实现。

事后又进一步研究了一下纯浏览器端的方案,SO上有这一条问答《HTML input type=file, get the image before submitting the form》:http://stackoverflow.com/questions/5802580/html-input-type-file-get-the-image-before-submitting-the-form ,里边推荐了一个GitHub的开源项目《jQuery File Upload》: http://blueimp.github.com/jQuery-File-Upload/ ,功能很全,也经过了完整的多浏览器兼容性测试,并不是所有的功能都可以被各种浏览器支持: https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support ,看起来现代浏览器们对HTML和新JS API支持的碎片化问题还是比较严重的。

最后还是要赞一下jQuery:有了jQuery,Javascript才能如此优雅。