重點講解了多文件上傳,大文件上傳,分塊上傳,斷點續傳,文件秒傳,上傳失敗自動修複再上傳等功能,上傳3、5個G,那都不是事。特別是大文件秒傳功能,不僅節省了上傳時間,還節省了網路帶寬和伺服器空間。 ...
我們平時做業務系統的時候,一般都會使用表單提交數據,提交數據的時候常常又伴隨著附件的上傳,如果附件小,用傳統的辦法就能解決,如果附件比較大,比如板給你安排一個上傳視頻的任務,視頻文件一般都比較大,動不動就是幾個G,用傳統的辦法就顯得非常有限。為了幫助大家解決大文件上傳的問題,我們特別編寫了這篇文章,重點講解了多文件上傳,大文件上傳,分塊上傳,斷點續傳,文件秒傳,上傳失敗自動修複再上傳等功能,上傳3、5個G,那都不是事。特別是大文件秒傳功能,不僅節省了上傳時間,還節省了網路帶寬和伺服器空間,採用md5加密標識文件唯一性,避免了同一文件的重覆上傳,上傳過程帶進度條,即時顯示上傳數據量和上傳百分比。求人不如求己,搞定這些技術,讓老闆加薪,再也不用低三下四的求人,大文件上傳無壓力,讓學習更高效,讓工作更輕鬆,媽媽再也不用擔心你的學習!
直接上代碼:
前臺前端index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>夜鷹教程網大文件分塊上傳</title> <style type="text/css"> * { font-family: "微軟雅黑"; margin: 0; padding: 0; } .container { padding-top: 10px; padding-left: 10px; } .container input { width: 120px; height: 30px; background-color: blue; color: white; border: 0; line-height: 30px; border-radius: 5px; margin-right: 5px; outline: none; cursor: pointer; } #filelist { width: 800px; border: solid 1px #eee; border-collapse: collapse; margin: 10px; } #filelist td { border-bottom: solid 1px #eee; height: 30px; font-size: 12px; /*line-height:30px ;*/ padding: 0 3px; } .filename { width: 200px; text-align: center; } .filestatus { width: 100px; text-align: center; } .fileprogress { text-align: center; } .domprogress { width: 320px; } .domsize { display: block; } #tdmsg { text-align: center; } #fileselect { display: none; } span.domtime { display:block; } </style> </head> <body> <div class="container"> <input type="file" name="fileselect" id="fileselect" value="" multiple/> <input type="button" id="btnselect" value="選擇上傳的文件" /> <input type="button" id="btnupload" value="開始上傳" /> </div> <table cellspacing="0" cellpadding="0" id="filelist"> <tr> <td class="filename">文件名</td> <td class="fileprogress">進度</td> <td class="filestatus">狀態</td> </tr> <!--<tr><td>人民的名義.avi </td><td><progress value="10" max="100" class="domprogress"></progress><span class="dompercent">10%</span><span class="domsize">0/1.86GB</span></td><td class="filestatus"><span class="domstatus">排隊中</span></td></tr>--> <tr id="trmsg"> <td colspan="3" id="tdmsg">請選擇要上傳的文件! 技術支持QQ:1416759661</td> </tr> </table> <script src="js/jquery-1.11.0.js" type="text/javascript" charset="utf-8"></script> <script src="js/spark-md5.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $("#btnselect").click(function() { $("#fileselect").click(); }); $("#fileselect").change(function() { var files = this.files; if(files.length > 0) { $("#trmsg").remove(); $(files).each(function(index, item) { console.log(index, item); var filesize = 0; if((item.size / 1024 / 1024 / 1024) >= 1) { filesize = (item.size / 1024 / 1024 / 1024).toFixed(2) + "GB"; // b=>kb=>mb=>gb } else if((item.size / 1024 / 1024 / 1024) < 1 && (item.size / 1024 / 1024) >= 1) { filesize = (item.size / 1024 / 1024).toFixed(2) + "MB"; } else if((item.size / 1024 / 1024) < 1 && (item.size / 1024) >= 1) { filesize = (item.size / 1024).toFixed(2) + "KB"; } else { filesize = item.size + "B"; } var htmlstr = '<tr><td>' + item.name + '</td><td><progress value="0" max="100" class="domprogress"></progress><span class="dompercent"> 0/'+filesize+'</span><span class="domtime">總共耗時:0 秒</span></td><td class="filestatus"><span class="domstatus">排隊中</span></td></tr>'; $("#filelist").append(htmlstr); }); } }); $("#btnupload").click(function() { var files = $("#fileselect")[0].files; $(files).each(function (index, item) { yyupload(files[index], $("span.domstatus").eq(index), $("span.dompercent").eq(index), $(".domprogress").eq(index), $("span.domtime").eq(index)); }); }); //文件上傳 function yyupload(file, dommsg, dompercentmb, domprogress, domtime, fn) { var startTime = new Date(); //獲取文件的md5字元串,用於標識文件的唯一性。 calculate(file); //獲取文件的加密字元串 function calculate(file) { var fileReader = new FileReader(); var chunkSize = 1024 * 1024 * 5; //每次讀取5MB var chunksCount = Math.ceil(file.size / chunkSize); //回大於參數x的最小整數 8=》8 8.4=》9 8.5=》9 -8.5=》-8 var currentChunk = 0; //當前塊的索引 var spark = new SparkMD5(); fileReader.onload = function(e) { console.log((currentChunk + 1) + "/" + chunksCount) dommsg.text("正在檢查文件: " + (currentChunk + 1) + "/" + chunksCount); spark.appendBinary(e.target.result); // 添加二進位字元串 currentChunk++; if(currentChunk < chunksCount) { loadNext(); } else { var md5value = spark.end(); console.log("文件加密結束,密鑰為:" + md5value); checkfile(md5value, file); //檢查伺服器是否存在該文件,存在就從斷點繼續上傳 } }; function loadNext() { var start = currentChunk * chunkSize; //計算讀取開始位置 var end = start + chunkSize >= file.size ? file.size : start + chunkSize; //計算讀取結束位置 fileReader.readAsBinaryString(file.slice(start, end)); //讀取為二進位字元串 }; loadNext(); } var repeatcount = 0; //檢查文件是否已經存在 function checkfile(md5value, file) { var fd = new FormData(); fd.append('rquesttype', "chekcfile"); fd.append('filename', file.name); fd.append('md5value', md5value); var xhr = new XMLHttpRequest(); xhr.open('post', 'FileUpoload.ashx', true); xhr.onreadystatechange = function(res) { if(xhr.readyState == 4 && xhr.status == 200) { var jsonobj = JSON.parse(xhr.responseText); //可以將json字元串轉換成json對象 //JSON.stringify(jsonobj); //可以將json對象轉換成json對符串 console.log("繼續上傳的位置:" + jsonobj.startindex); switch(jsonobj.flag) { case "0": doUpload(md5value, file, 0); break; case "1": doUpload(md5value, file, parseInt(jsonobj.startindex)); break; case "2": secondUpload(file); break; } repeatcount = 0; } else if(xhr.status == 500) { setTimeout(function() { if(repeatcount < 3) { checkfile(md5value, file); } repeatcount++; }, 3000); } } //開始發送 xhr.send(fd); } //實現秒傳功能 function secondUpload(file) { var timerange = (new Date().getTime() - startTime.getTime()) / 1000; domtime.text("耗時" + timerange + "秒"); //顯示結果進度 var percent =100; dommsg.text(percent.toFixed(2) + "%"); domprogress.val(percent); var total = file.size; if (total > 1024 * 1024 * 1024) { dompercentmb.text((total / 1024 / 1024 / 1024).toFixed(2) + "GB/" + (total / 1024 / 1024 / 1024).toFixed(2) + "GB"); } else if (total > 1024 * 1024) { dompercentmb.text((total / 1024 / 1024).toFixed(2) + "MB/" + (total / 1024 / 1024).toFixed(2) + "MB"); } else if (total > 1024 && total < 1024 * 1024) { dompercentmb.text((total / 1024).toFixed(2) + "KB/" + (total / 1024).toFixed(2) + "KB"); } else { dompercentmb.text((total).toFixed(2) + "B/" + (total).toFixed(2) + "B"); } } //上傳文件 function doUpload(md5value,file,startindex) { var reader = new FileReader();//新建一個讀文件的對象 var step = 1024 * 200; //每次讀取文件大小 200KB var cuLoaded = startindex; //當前已經讀取總數 var total = file.size;//文件的總大小 //讀取一段成功 reader.onload = function (e) { //處理讀取的結果 var result = reader.result; //本次讀取的數據 var loaded = e.loaded; //本次讀取的數據長度 uploadFile(result, cuLoaded, function () { //將分段數據上傳到伺服器 cuLoaded += loaded; //如果沒有讀完,繼續 var timerange = (new Date().getTime() - startTime.getTime()) / 1000; if (total > 1024 * 1024 * 1024) { dompercentmb.text((cuLoaded / 1024 / 1024 / 1024).toFixed(2) + "GB/" + (total / 1024 / 1024 / 1024).toFixed(2) + "GB"); } else if (total > 1024 * 1024) { dompercentmb.text((cuLoaded / 1024 / 1024).toFixed(2) + "MB/" + (total / 1024 / 1024).toFixed(2) + "MB"); } else if (total > 1024 && total < 1024 * 1024) { dompercentmb.text((cuLoaded / 1024).toFixed(2) + "KB/" + (total / 1024).toFixed(2) + "KB"); } else { dompercentmb.text((cuLoaded).toFixed(2) + "B/" + (total).toFixed(2) + "B"); } domtime.text("耗時" + timerange + "秒"); if (cuLoaded < total) { readBlob(cuLoaded); } else { console.log('總共用時:' + timerange); cuLoaded = total; sendfinish(); //告知伺服器上傳完畢 domtime.text("上傳完成,總共耗時" + timerange + "秒"); } //顯示結果進度 var percent = (cuLoaded/total) * 100; dommsg.text(percent.toFixed(2) + "%"); domprogress.val(percent); }); } var k = 0; function sendfinish() { var fd = new FormData(); fd.append('rquesttype', "finishupload"); fd.append('filename', file.name); fd.append('md5value', md5value); fd.append('totalsize', file.size); var xhr = new XMLHttpRequest(); xhr.open('post', 'FileUpoload.ashx', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { if (fn) { fn(); //如果上傳成功,繼續上傳下一個文件 } k = 0; } else if (xhr.status == 500) { setTimeout(function () { if (k < 3) { sendfinish(); //上傳完畢的前端處理 } k++ }, 3000); } } //開始發送 xhr.send(fd); } var m = 0; //關鍵代碼上傳到伺服器 function uploadFile(result, startIndex, onSuccess) { var blob = new Blob([result]); //提交到伺服器 var fd = new FormData(); fd.append('file', blob); fd.append('rquesttype',"uploadblob"); fd.append('filename', file.name); fd.append('md5value', md5value); fd.append('loaded', startIndex); var xhr = new XMLHttpRequest(); xhr.open('post', 'FileUpoload.ashx', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { m = 0; if (onSuccess) onSuccess(); } else if (xhr.status == 500) { setTimeout(function () { if (m < 3) { containue(); m++; } }, 1000); } } //開始發送 xhr.send(fd); } //指定開始位置,分塊讀取文件 function readBlob(start) { //指定開始位置和結束位置讀取文件 var blob = file.slice(start, start + step); //讀取開始位置和結束位置的文件 reader.readAsArrayBuffer(blob); //讀取切割好的文件塊 } //繼續 function containue() { readBlob(cuLoaded); } readBlob(cuLoaded); } } </script> </body> </html>
需要到網上下載一個spark-md5.js文件,下載地址:
https://raw.githubusercontent.com/satazor/SparkMD5/master/spark-md5.js
後端處理請求的文件FileUpoload.ashx代碼:
<%@ WebHandler Language="C#" Class="FileUpoload" %> using System; using System.Web; using System.IO; using Microsoft.VisualBasic.Devices; public class FileUpoload : IHttpHandler { private string basefilename = HttpContext.Current.Server.MapPath("/FileTemp/"); private long totalCount = 0; public void ProcessRequest (HttpContext context) { HttpRequest req=context.Request; string rquesttype=req.Form["rquesttype"]; switch (rquesttype) { case "chekcfile": chekcfile(req); break; case "uploadblob": uploadblob(req); break; case "finishupload": finishupload(req); break; } } /// <summary> /// 結束文件上傳,修改上傳後的名稱 /// </summary> /// <param name="req"></param> public void finishupload(HttpRequest req) { //接收前端傳遞過來的參數 var md5value = req.Form["md5value"];//文件md5加密的字元串 var filename = req.Form["filename"];//文件的名稱 var totalsize = req.Form["totalsize"];//文件的總大小 string fullname = basefilename + md5value + ".part";//上傳的時候的名稱 string okname = basefilename + md5value + ".ok";//上傳完畢之後再該目錄下創建一個.ok尾碼的文件,這個文件的大小為0,沒有內容,不占空間,主要用於前端檢查這個文件是否已經存在了 var oldname = basefilename + filename; Computer MyComputer = new Computer(); try { File.Create(okname); FileInfo fi = new FileInfo(oldname); if (fi.Exists) { fi.Delete(); } MyComputer.FileSystem.RenameFile(fullname, filename); } catch (Exception ex) { } finally { var str = string.Format("{{\"data\":\"ok\"}}"); HttpContext.Current.Response.Write(str); } } /// <summary> /// 處理文件分塊上傳的數據 /// </summary> /// <param name="req"></param> public void uploadblob(HttpRequest req) { if (req.Files.Count <= 0) { HttpContext.Current.Response.Write("獲取伺服器上傳文件失敗"); return; } HttpPostedFile _file = req.Files[0]; //獲取參數 string filename = req.Form["filename"]; string md5value = req.Form["md5value"]; var tempfilename = md5value + ".part"; //如果是int 類型當文件大的時候會出問題 最大也就是 1.9999999990686774G long loaded = Convert.ToInt64(req.Form["loaded"]); totalCount += loaded; string newname = basefilename + tempfilename; Stream stream = _file.InputStream; if (stream.Length <= 0) throw new Exception("接收的數據不能為空"); byte[] dataOne = new byte[stream.Length]; stream.Read(dataOne, 0, dataOne.Length); FileStream fs; try { fs = new FileStream(newname, FileMode.Append, FileAccess.Write, FileShare.Read, 1024); fs.Write(dataOne, 0, dataOne.Length); fs.Close(); } catch (Exception ex) { } finally { stream.Close(); //檢查文件已經上傳的大小是否等於文件的總大小 } HttpContext.Current.Response.Write("分段數據保存成功"); } /// <summary> /// 檢查文件是否存在 /// </summary> /// <param name="req"></param> public void chekcfile(HttpRequest req) { var md5value = req.Form["md5value"];//得到前端傳遞過來的文件的mdf字元串 var path_ok = basefilename + md5value + ".ok"; var path_part = basefilename + md5value + ".part"; int flag = 0; string json = string.Empty; if (File.Exists(path_ok))//傳完了 { flag = 2; json = string.Format("{{\"flag\":\"{0}\"}}", flag); } else if (File.Exists(path_part))//傳了一部分 { flag = 1; var startindex = new FileInfo(path_part).Length.ToString(); json = string.Format("{{\"flag\":\"{0}\",\"startindex\":\"{1}\"}}", flag, startindex); } else//新文件 { flag = 0; json = string.Format("{{\"flag\":\"{0}\",\"startindex\":\"0\"}}", flag); } HttpContext.Current.Response.Write(json); } public bool IsReusable { get { return false; } } }
如果看不懂以上代碼,我們還有配套的視頻教程,如果需要視頻教程可以咨詢QQ:1416759661