Flask: 跨域上傳的回調方案

隨着互聯網業務的規模不斷擴大,網站的開發方式也產生髮巨大的變化。就拿上傳來講,早些年一般都是網站自身包含上傳模塊,經過跳轉方式或iframe方式進行上傳。這沒有涉及到跨域,因此對於上傳結果的通知是很容易作到的。然而如今愈來愈多的作法是將上傳功能分離開來,造成獨立的上傳域來提供上傳服務。本文主要是針對跨域上傳的上傳結果通知分析並提供解決方案。javascript

跨域上傳的方式

  1. 使用第三方存儲,如七牛雲存儲html

  2. 重定向上傳方式前端

  3. iframe上傳方式java

使用第三方存儲

好比使用七牛雲存儲,這種方式第三方通常會提供各個語言的SDK,若是正巧您所使用的語言沒有相應的SDK,那就只能本身去實現了。實現起來倒也不難,無非就是實現第三方受權的流程,而後經過文件上傳協議提交文件到第三方。這種方式通常是經過服務器端來完成的,上傳完成後第三方會有個上傳反饋,而後返回到前端。
python

重定向上傳方式

這是最傳統的上傳方式。各類上傳方案均可以很容易的實現它。將上傳表單重定向到上傳網址,完成上傳後可攜帶參數跳轉回來。
jquery

iframe上傳方式

這種方式是在頁面中嵌入一個隱藏的iframe,上傳表單提交到iframe來完成上傳。現代瀏覽器都比較好的支持了iframe,因此這個方式也是通用的。這也是本文將討論的上傳方案。
flask

iframe上傳完成如何通知

同域或子域方式

若是是同域方式,由於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>

大體上就是這樣,這是很經常使用的跨域上傳方案,有必要認真理解並加以實踐。

相關文章
相關標籤/搜索