
使用 iframe 上传是很传统的异步上传的方式。我之前从来没有用过。
最近遇到一个问题是,有用户抱怨说图片上传不了。当然我测试是没有问题的。
之前的代码是同事基于 七牛的异步上传示例的代码修改而来的,我接着用了。
由于上传跟后台交互并不多,用户也没有提供更多的信息。为了快速简单的解决这个问题,我干脆使用比较传统的上传方式算了。
我们知道如果直接提交一个表单,当请求结果返回的时候,当前页面的内容会被响应内容替换掉。
简单来说,就是页面刷新了。
我们希望对此进行改进,希望上传操作在后台进行,然后上传完成之后通知我们。
基于 iframe 的异步上传,我认为关键在于设置 target 属性,
将 <form> 的请求响应结果显示到指定的 <iframe> 中。
查看 <form> 中 target 属性的文档 有如下说明:
A name or keyword indicating where to display the response that is received after submitting the form.
iframename: The response is displayed in a named <iframe>.
我们可以通过设置 <form>的 target 属性。将其提交结果显示到指定的 iframe中。而不会刷新当前页面。
也就给我们感觉是在后台进行。(当然 target 属性还有更多其他属性,特别是在 HTML 5 中。)
上面解决了后台上传的问题,另一个问题就是上传完成的通知了。
我们什么时候获得上传完成的响应结果?
一般的做法是,上传完成之后,响应的内容返回一段 JS ,然后其中的 JS 回调指定的处理函数 。
于是得到如下相关 前端 代码
<form method="post" id="tr-upload-form" action="/awards/api/upload_file" enctype="multipart/form-data" target='tr_upload_frame'> <input type="file" name="file" accept="image/jpeg,image/gif,image/png" class="form-control"> <input type="submit" class="btn btn-primary form-control" value='上传'> </form> <iframe style="display:none" name="tr_upload_frame"></iframe> 下面的处理表单提交的 JS ,当用户点击上传时,动态替换 <form> 的 action 属性,加上回调参数 。
var $_tr_upload_form = $('#tr-upload-form'); $_tr_upload_form.on('submit',function(){ var seed = Math.floor(Math.random() * 1000); var callback = 'upload_cb_'+seed; var url = "/awards/api/upload_file"; $_tr_upload_form.attr('action', url+'?callback='+callback); window[callback] = function(data){ console.log('received callback:',data); $_tr_upload_form.attr('action',url); delete window[callback]; if(data.ok){ form.avatar = data.key; alert("上传成功") }else{ alert("上传失败"); } }; }); 后台处理上传,我是用 Flask 和 七牛 Python-SDK-6
Flask 的 file 属性的 FileStorage 的 stream 实例可以直接用于上传这点很方便。
响应的结果就是一段带 JS 回调代码的脚本。
@app.route('/api/upload_file',methods=['POST']) def api_upload_file(): from qiniu.io import PutExtra,put import uuid callback = request.args.get('callback') file = request.files['file'] uptoken = _create_uptoken() key = uuid.uuid4().hex extra = PutExtra() extra.mime_type = file.mimetype ret,err = put(uptoken,key,file.stream,extra) ok = True if err is not None: logger.error("uploa_file error "+str(err)) ok = False data = { "key":key, "ok":ok } text = ''' <script> window.top.window['{callback}']({data}); </script> '''.format(callback=callback,data=json.dumps(data)) return text 1 onlyxuyang 2015 年 10 月 28 日 via Android 七牛太贵了 不是商业应用根本搞不起 |
2 bdbai 2015 年 10 月 28 日 via iPhone @onlyxuyang 算上免费额度的话,小微级应用还是可以接受的,毕竟他们服务很棒。 |
3 5thcat 2015 年 10 月 29 日 FormData + ajax 不行吗?要兼容老浏览器? |
4 typcn 2015 年 10 月 29 日 什么年代了。。。。。。。。你的用户用的是 IE6 么。。。 |
5 verytoex 2015 年 10 月 29 日 @onlyxuyang 免费版好像有 10G 流量,小站基本够用 |