上篇文章說明了如何用js在用戶端上傳檔案,功能是實作了,但當上傳大檔案時,一是時間長,像卡死了一樣,二是使用者不知道進度很容易在沒上傳完之前就關掉。今天我們就來看如何在用戶端将大檔案分片上傳,并顯示進度。
先來看一下效果:
1、建立一個"js_upBigFile"的項目,添加一個webForm1.aspx,給WebForm1添加如下控件,這裡進度條用一個就可以,我這裡是示範如何用進度條控件progress和用div模拟兩種方式顯示。Form外面的基本資訊這些,都是用來監控效果的控件,實際應用時是不需要的。
<body>
<form id="form1" runat="server">
<input type="file" id="f" />
<br />
<input type="button" value="up" onclick="up()" />
<br />
<!--進度條标簽-->
<progress value="0" max="100" id="progress" style="height: 20px; width: 100%"></progress>
<br />
<!--div模拟進度條-->
<div id="progressNumber" style="width: 0%; height: 20px; background-color: Red"></div>
<br />
<div id="result"></div>
</form>
<p>
基本資訊:<input id="Text1" type="text" /></p>
<p>
正在上傳:<input id="Text2" type="text" /></p>
<p>
正在處理:<input id="Text3" type="text" /></p>
<p>
<textarea id="TextArea1" name="S1"></textarea></p>
<p>
<textarea id="TextArea2" name="S2" cols="20" rows="1"></textarea></p>
</body>
2、在項目目錄下建立兩個檔案夾,一個“1”來存儲臨時檔案,一個“2”來存儲上傳後的檔案。注意不是“bin"目錄。
3、添加點選按鈕的js代碼
這個按鈕就是點選執行js代碼的按鈕,<input type="button" value="up" onclick="up()" />
js函數up()的代碼如下:
<script type="text/ecmascript">
//每個切片檔案的大小 1024 * 1024 就是1M,下面我這裡就是2M
var bytesPerPiece = 1024 * 1024 * 2;
var totalPieces;
pieceover = 0;
hbindex = 0;
totalPieces = 0;
function up() {
if (document.getElementById("f").value == "") {
document.getElementById("result").innerHTML = "請選擇檔案";
}
else {
var fileObj = document.getElementById("f").files[0];
var start = 0;
var end;
var index = 0;
var filesize = fileObj.size;
var filename = fileObj.name;
//擷取最後一個.的位置
var exindex = filename.lastIndexOf(".");
//擷取擴充名,帶.
var fileexname = filename.substr(exindex);
//計算檔案切片總數,也就是一共多少個檔案
totalPieces = Math.ceil(filesize / bytesPerPiece);
infostr = "";
infostr += " 檔案名:" + filename;
infostr += " 大小:" + filesize;
infostr += " 總數:" + totalPieces;
document.getElementById("Text1").value = infostr;
while (start < filesize) {
//分片結束大小位置
end = start + bytesPerPiece;
if (end > filesize) {
end = filesize;
}
//建立xhr
var xhr = new XMLHttpRequest();
//對應的處理資料的 ashx 檔案 ------------- 注意這裡
var url = "upFile.ashx";
//得到分片檔案,根據開始位置和結束位置
var chunk = fileObj.slice(start, end);
var sliceIndex = fileObj.name + index;
//FormData對象
var fd = new FormData();
//檔案素引,也就是目前是第幾個片檔案
fd.append("index", index);
//将分片檔案做為參數傳到ashx檔案中上傳
fd.append("file", chunk, filename);
//加上目前的時間,據稱可以防止檔案緩存,我沒有試過,反正寫上吧
fd.append("time", new Date().toString());
xhr.open("POST", url, true);
xhr.onreadystatechange = function () {
//全部上傳完畢以後,彈出菜單
if (xhr.readyState == 4 && xhr.status == 200) {
alert("分片結束");
}
}
//上傳結束後,注意這裡是上傳一個就會運作一次,
//也正是這種機制使我們可以監控上傳進度
xhr.upload.onloadend = function (evt) {
pieceover++;
document.getElementById("Text2").value = pieceover;
var percentComplete = (pieceover / totalPieces * 100).toFixed(4);
document.getElementById('progress').value = percentComplete;
document.getElementById('progressNumber').style.width = percentComplete + "%";
document.getElementById("result").innerHTML = "第1步(共2步):已經完成:" + percentComplete + "%";
}
//發送資料
xhr.send(fd);
//更改下次分片檔案的起始位置
start = end;
index++;
}
}
}
</script>
4、添加一個upFile.ashx一般處理程式
代碼如下:
using System;
using System.Web;
namespace WebApplication1
{
/// <summary>
/// upFile 的摘要說明
/// </summary>
public class upFile : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//服務端存儲分片檔案的位址
string rootpath = System.Web.HttpContext.Current.Server.MapPath("~");
string savePath = rootpath + "\\1\\";
//得到分片檔案的索引名
string index = context.Request["index"];
//得到傳過來的分片檔案
HttpPostedFile file = context.Request.Files[0];
//檔案擴充名
string fileType = System.IO.Path.GetExtension(file.FileName);
//存到檔案伺服器的檔案名稱 用目前時間命名
//string fileNewName = DateTime.Now.ToString("yyyyMMddHHmmss_fff") + fileType;
string fileNewName = index + fileType;
try
{
//上傳
file.SaveAs(savePath + fileNewName);
context.Response.Write("上傳成功!");
}
catch (Exception ex)
{
context.Response.Write("上傳失敗!錯誤資訊:" + ex.Message.ToString());
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
此時第一步分片上傳已經結束,運作效果如下:
1檔案夾中的内容
5、通過上面4步,我們就将檔案分片上傳到伺服器上了,這就完成了第一步,現在我們做第二步,在伺服器上把檔案合并起來。
在js中新加幾個變量
pieceover = 0; //已經合并的檔案總數
hbindex = 0; //目前正在合并的索引号
totalPieces = 0; //總共要合并的檔案總數
将up函數中結束的部分換成如下代碼,再用go()函數開始合并
xhr.onreadystatechange = function () {
//全部上傳完畢以後,彈出菜單
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
if (pieceover == totalPieces) { //如果還沒有開始合并
pieceover = 0; //從第0片開始合并
//重置滾動條
document.getElementById('progress').value = 0;
//開始合并
go(0, filename, fileexname);
}
}
}
6、go函數代碼:
function go(index, newfilename, exname) {
var xhr1 = new XMLHttpRequest();
var url1 = "HeBin.ashx"; //這次用一個新的處理檔案
var fd1 = new FormData();
xhr1.open("POST", url1, true);
xhr1.onreadystatechange = function () {
if (xhr1.readyState == 4 && xhr1.status == 200) {
var result1 = xhr1.responseText;
if (result1 != "") {
if (pieceover < totalPieces) {
if (result1 == hbindex) {
document.getElementById("TextArea2").value += "Y(result1:" + result1 + ",hbindex:" + hbindex + ")";
pieceover++;
document.getElementById("Text3").value = pieceover;
//var percentComplete = Math.round(pieceover * 100 / totalPieces)
var percentComplete = (pieceover / totalPieces * 100).toFixed(4);
document.getElementById('progress').value = percentComplete;
document.getElementById('progressNumber').style.width = percentComplete + "%";
document.getElementById("result").innerHTML = "第2步(共2步):已經完成:" + percentComplete + "%";
hbindex++;
document.getElementById("TextArea1").value += hbindex;
go(hbindex, newfilename, exname);
document.getElementById("TextArea1").value += "over,result:" + result1;
}
}
else {
document.getElementById("result").innerHTML = "上傳成功!";
}
}
else
{
if (pieceover >= totalPieces) {
document.getElementById("result").innerHTML = "上傳成功!";
}
}
}
}
fd1 = new FormData();
fd1.append("newfilename", newfilename); //合并成的檔案名
fd1.append("filename", hbindex); //檔案名(索引)
fd1.append("exname", exname); //擴充名
fd1.append("acttime", new Date().toString());
xhr1.send(fd1);
}
7、再添加一個HeBin.ashx,用來把上傳上去的分片檔案合并,代碼如下
using System;
using System.IO;
using System.Web;
namespace WebApplication1
{
/// <summary>
/// HeBin 的摘要說明
/// </summary>
public class HeBin : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string NewFileName = context.Request["newfilename"];
//伺服器路徑
string rootpath = System.Web.HttpContext.Current.Server.MapPath("~");
//臨時檔案路徑
string qiepianpath = rootpath + "1\\";
//要處理的檔案名,不帶擴充名,如 0 1
string nowname = context.Request["filename"];
//擴充名
string fileType = context.Request["exname"];
//要處理的檔案名,帶擴充名,如 0.mp4
string nownameall = nowname + fileType;
//要處理的檔案完整路徑
string thepath = qiepianpath + nownameall;
//調試用,萬一哪裡出錯了,可以根據這個數字看看到底是哪個檔案錯了
int time = 0;
try
{
string filename = NewFileName;
string SaveFileName = rootpath + "2\\" + filename;
using (FileStream fs = new FileStream(SaveFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (FileStream readStream = new FileStream(thepath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[readStream.Length];
int data = 0;
//合并檔案
while ((data = readStream.Read(buffer, 0, 1024)) > 0)
{
fs.Position = fs.Length;
fs.Write(buffer, 0, data);
}
}
fs.Dispose();
File.Delete(thepath);
context.Response.Write(nowname);
}
}
catch (Exception x)
{
time++;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}