在網頁中直接上傳大文件一直是個比較頭疼的問題,主要面臨的問題通常包括兩類:一是上傳時間長中途一旦出錯會致使前功盡棄;二是服務端配置複雜,要考慮接收超大表單和超時問題,若是是託管主機沒準還改不了配置,默認只能接收小於4MB的附件。 html
比較理想的方案是可以把大文件分片,一片一片的傳到服務端,再由服務端合併。這麼作的好處在於一旦上傳失敗只是損失一個分片而已,不用整個文件重傳,並且每一個分片的大小能夠控制在4MB之內,服務端不用作任何設置就可適應。 html5
經常使用的解決方案是RIA,以flex爲例,一般是利用FileReference.load方法加載文件獲得ByteArray,而後分片構造表單(flash的高版本不容許直接訪問文件)。不過這個load方法只能加載較小的文件,大約不超過300MB,所以適用性不是很強。 jquery
好在如今有了HTML5,咱們能夠直接構造分片了,這是一個很是喜人的進步,只惋惜目前適用面不廣(IE啊IE,真是恨你恨得牙癢癢)。 linux
言歸正傳,來看一個DEMO吧,基於ASP.Net MVC3,只是示例,不少問題作了簡化處理。 ajax
主要是客戶端,新特性都體如今這裏: 安全
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> 服務器
<!DOCTYPE html> 併發
<html lang="zh-CN"> app
<head> 異步
<meta charset="utf-8">
<title>HTML5大文件分片上傳示例</title>
<script src="../Scripts/jquery-1.11.1.min.js"></script>
<script>
var page = {
init: function(){
$("#upload").click($.proxy(this.upload, this));
},
upload: function(){
var file = $("#file")[0].files[0], //文件對象
name = file.name, //文件名
size = file.size, //總大小
succeed = 0;
var shardSize = 2 * 1024 * 1024, //以2MB爲一個分片
shardCount = Math.ceil(size / shardSize); //總片數
for(var i = 0;i < shardCount;++i){
//計算每一片的起始與結束位置
var start = i * shardSize,
end = Math.min(size, start + shardSize);
//構造一個表單,FormData是HTML5新增的
var form = new FormData();
form.append("data", file.slice(start,end)); //slice方法用於切出文件的一部分
form.append("name", name);
form.append("total", shardCount); //總片數
form.append("index", i + 1); //當前是第幾片
//Ajax提交
$.ajax({
url: "../File/Upload",
type: "POST",
data: form,
async: true, //異步
processData: false, //很重要,告訴jquery不要對form進行處理
contentType: false, //很重要,指定爲false才能造成正確的Content-Type
success: function(){
++succeed;
$("#output").text(succeed + " / " + shardCount);
}
});
}
}
};
$(function(){
page.init();
});
</script>
</head>
<body>
<input type="file" id="file" />
<button id="upload">上傳</button>
<span id="output" style="font-size:12px">等待</span>
</body>
</html>
這裏的slice方法和FormData都是html5以前不存在的。經過這樣的方法,咱們的表單構造出來是這樣的,抓包看看:
能夠看到構造出來的Content-Type是multipart/form-data,也就是符合RFC標準的那個最傳統的文件上傳表單。另外咱們同時傳輸的name、total等屬性也都在表單裏。
而後是服務端,沒什麼新鮮的,徹底是在接收一個普通的文件:
[HttpPost]
public ActionResult Upload()
{
//從Request中取參數,注意上傳的文件在Requst.Files中
string name = Request["name"];
int total = Convert.ToInt32(Request["total"]);
int index = Convert.ToInt32(Request["index"]);
var data = Request.Files["data"];
//保存一個分片到磁盤上
string dir = Server.MapPath("~/Upload");
string file = Path.Combine(dir, name + "_" + index);
data.SaveAs(file);
//若是已是最後一個分片,組合
//固然你也能夠用其它方法好比接收每一個分片時直接寫到最終文件的相應位置上,但要控制好併發防止文件鎖衝突
if(index == total)
{
file = Path.Combine(dir, name);
var fs = new FileStream(file, FileMode.Create);
for(int i = 1;i <= total;++i)
{
string part = Path.Combine(dir, name + "_" + i);
var bytes = System.IO.File.ReadAllBytes(part);
fs.Write(bytes, 0, bytes.Length);
bytes = null;
System.IO.File.Delete(part);
}
fs.Close();
}
//返回是否成功,此處作了簡化處理
return Json(new { Error = 0 });
}
上面的DEMO不少問題是簡化處理的,好比沒作什麼異常處理,客戶端也沒有判斷服務端是否出錯重試一類的,各位能夠本身完善。
在上面的基礎上,咱們能夠作不少功能上的擴展,好比咱們能夠控制全部分片是順序上傳仍是併發上傳,以適用不一樣應用。再好比咱們能夠在總體文件上傳前以及分片上傳前都先計算一下相應的HASH,發個請求詢問服務器文件是否已存在,若是存在就不要重複上傳了,這樣就實現了「極速上傳」以及「斷點續傳」。
HTML5 地理位置定位(HTML5 Geolocation)原理及應用 http://www.linuxidc.com/Linux/2012-07/65129.htm
HTML5移動開發即學即用(雙色) PDF+源碼 http://www.linuxidc.com/Linux/2013-09/90351.htm
HTML5入門學習筆記 http://www.linuxidc.com/Linux/2013-09/90089.htm
HTML5移動Web開發筆記 http://www.linuxidc.com/Linux/2013-09/90088.htm
HTML5 開發中的本地存儲的安全風險 http://www.linuxidc.com/Linux/2013-06/86486.htm
《HTML5與CSS3權威指南》及相配套源碼 http://www.linuxidc.com/Linux/2013-02/79950.htm
關於 HTML5 使人激動的 10 項預測 http://www.linuxidc.com/Linux/2013-02/79917.htm
HTML5與CSS3實戰指南 PDF http://www.linuxidc.com/Linux/2013-02/79910.htm