隨着互聯網業務的規模不斷擴大,網站的開發方式也產生髮巨大的變化。就拿上傳來講,早些年一般都是網站自身包含上傳模塊,經過跳轉方式或iframe方式進行上傳。這沒有涉及到跨域,因此對於上傳結果的通知是很容易作到的。然而如今愈來愈多的作法是將上傳功能分離開來,造成獨立的上傳域來提供上傳服務。本文主要是針對跨域上傳的上傳結果通知分析並提供解決方案。javascript
使用第三方存儲,如七牛雲存儲html
重定向上傳方式前端
iframe上傳方式java
好比使用七牛雲存儲,這種方式第三方通常會提供各個語言的SDK,若是正巧您所使用的語言沒有相應的SDK,那就只能本身去實現了。實現起來倒也不難,無非就是實現第三方受權的流程,而後經過文件上傳協議提交文件到第三方。這種方式通常是經過服務器端來完成的,上傳完成後第三方會有個上傳反饋,而後返回到前端。
python
這是最傳統的上傳方式。各類上傳方案均可以很容易的實現它。將上傳表單重定向到上傳網址,完成上傳後可攜帶參數跳轉回來。
jquery
這種方式是在頁面中嵌入一個隱藏的iframe,上傳表單提交到iframe來完成上傳。現代瀏覽器都比較好的支持了iframe,因此這個方式也是通用的。這也是本文將討論的上傳方案。
flask
若是是同域方式,由於iframe是嵌入在網頁中,因此能夠在iframe上傳完成後經過parent來調用父框架提供的方法並將參數傳入來實現通知,或者能夠在上傳完成後父框架獲取iframe的引用並獲得iframe的輸出內容來實現通知。而若是是子域的方式,則還須要設置document.domain爲子域。
跨域
由於是跨域方式,這將致使上傳完成後目標iframe沒有權限去調用父框架中的方法,而且父框架也沒有權限去訪問子框架的內容,因此沒法進行通知上傳結果。瀏覽器
是的,通常來講,在iframe上傳完成後都會輸出數據或一段js代碼。可是若是輸出的是一個iframe呢?本文將用2個host來演示這種狀況。www.aw.com 是須要上傳的網站,www.file.com 是提供上傳的網站。咱們來看個圖:七牛雲存儲
上圖很清晰的體現了這種包含關係。A上傳到B,B上傳完成後輸出iframe,並指向A域名的一個頁面,也就是上圖中的C。A和C是同域的,即便是A不能訪問C,可是C卻能夠經過parent.parent來訪問A。爲了演示,我配置瞭如下host:
aw目錄結構以下:
www/aw
----app.py
----templates
--------index.html
--------cross.html
app.py源碼:
#coding: utf8 import os from datetime import timedelta from flask import Flask, render_template import urllib app = Flask(__name__) app.secret_key = os.urandom(24) app.permanent_session_lifetime = timedelta(seconds=24 * 60 * 60) @app.route('/') def index(): return render_template('index.html', **dict(upload_url='http://www.file.com:6667/upload')) @app.route('/cross.html') def cross(): return render_template('cross.html') if __name__ == '__main__': app.run( host="0.0.0.0", port=int("6666"), debug=True )
index.html源碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>aw</title> <meta name="author" content="" /> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <meta name="keywords" content="aw" /> <meta name="description" content="aw" /> <script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.1.0/jquery.min.js"></script> </head> <body> <form enctype="multipart/form-data" action="{{ upload_url }}" method="POST" target="upload_iframe"> <input name="UploadName" type="file" /> <input type="submit" value="上傳" /> </form> <iframe id="upload_iframe" name="upload_iframe" width="0" height="0" style="display: none;"></iframe> <script type="text/javascript"> // 上傳完成回調 function uploadComplete(data) { console.log('上傳完成:' + data); } </script> </body> </html>
cross.html源碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>aw cross</title> <meta name="author" content="" /> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <meta name="keywords" content="aw cross" /> <meta name="description" content="aw cross" /> <script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.1.0/jquery.min.js"></script> </head> <body> <script type="text/javascript"> $(function() { var href = location.href; var index = href.indexOf('?'); if (index < 0) { var data = 'result=upload_error'; } else { var data = href.substring(index + 1); } // 將數據經過調用parent.parent.uploadComplete方法來傳遞 parent.parent.uploadComplete(data); }); </script> </body> </html>
file目錄結構以下:
www/file
----app.py
app.py源碼:
#coding: utf8 import os from datetime import timedelta from flask import Flask, request, render_template import urllib app = Flask(__name__) app.secret_key = os.urandom(24) app.permanent_session_lifetime = timedelta(seconds=24 * 60 * 60) @app.route('/upload', methods=['POST']) def index(): '''忽略上傳文件處理細節,直接返回數據''' cross_html = 'http://www.aw.com:6666/cross.html?result=upload_complete' return '<iframe width="0" height="0" style="display:none;" src="%s"></iframe>' % cross_html if __name__ == '__main__': app.run( host="0.0.0.0", port=int("6667"), debug=True )
這樣就實現了跨域回調,知道了原理其實就不難了,若有須要可進行封裝,如實現跨域檢測,若是是同域則使用同域的方式進行回調,若是是跨域,則使用跨域的方式進行回調。而且可實現多文件上傳方案。
上傳超時是必須處理的上傳錯誤之一。對於iframe上傳(這裏並無特指同域或跨域)來講,超時機制將會限制整個上傳所佔用的時間,這是有必要的,不然若是網絡異常,前端將一直顯示正在上傳中,這是極不友好的用戶體驗。另外一方面也是爲了安全考慮。
其實實現iframe的上傳超時機制很是簡單。經過setTimeout在固定時間後去remove iframe,固然若是上傳是成功的則會提早clearTimeout。因此若是setTimeout成功執行則確定是超時了,不然就是上傳完成(至因而上傳成功或上傳失敗則由服務器端返回的狀態碼來肯定)。好比:
index.html源碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>aw</title> <meta name="author" content="" /> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <meta name="keywords" content="aw" /> <meta name="description" content="aw" /> <script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.1.0/jquery.min.js"></script> </head> <body> <form enctype="multipart/form-data" action="{{ upload_url }}" method="POST" target="upload_iframe"> <input name="UploadName" type="file" /> <input type="submit" value="上傳" /> </form> <iframe id="upload_iframe" name="upload_iframe" width="0" height="0" style="display: none;"></iframe> <script type="text/javascript"> // 上傳完成回調 function uploadComplete(data) { timeout_id && clearTimeout(timeout_id); console.log('上傳完成:' + data); } var timeout_id = setTimeout(function() { $("#upload_iframe").remove(); uploadComplete("上傳超時") }, 10000); </script> </body> </html>
大體上就是這樣,這是很經常使用的跨域上傳方案,有必要認真理解並加以實踐。