參考代碼:https://blog.csdn.net/tangyan1207/article/details/74937645javascript
解決了FileReader不支持readAsBinaryString方法的問題, 解決了MD5相同的問題css
HTML+JS部分:html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width" /> <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; } .fileselectchk { width: 100px; 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="fileselectchk">選擇</td> <td class="filename">文件名</td> <td class="fileprogress">進度</td> <td class="filestatus">狀態</td> </tr> </table> <script src="~/Scripts/jquery-3.3.1.js"></script> <script src="~/Scripts/SparkMd5.js"></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 style="text-align:center"><input type="checkbox" name="fileChk" value="' + index +'" /></td><td style="text-align:center">' + 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); }); } }); $('fileChk').each(function (index, item) { }); $("#btnupload").click(function () { var files = $("#fileselect")[0].files; $(files).each(function (index, item) { console.log('begin'); console.log(index); 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(); var fileReader = new FileReader(); 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; //當前塊的索引 fileReader.onload = function (e) { console.log((currentChunk + 1) + "/" + chunksCount) dommsg.text("正在檢查文件: " + (currentChunk + 1) + "/" + chunksCount); console.log(e.target.result); currentChunk++; if (currentChunk < chunksCount) { console.log('currentChunk'); console.log(currentChunk); loadNext(); } else { var md5value = SparkMD5.ArrayBuffer.hash(e.target.result); console.log("文件加密結束,密鑰爲:" + md5value); checkfile(md5value, file); //檢查服務器是否存在該文件,存在就從斷點繼續上傳 } }; loadNext(); function loadNext() { var start = currentChunk * chunkSize; //計算讀取開始位置 var end = start + chunkSize >= file.size ? file.size : start + chunkSize; //計算讀取結束位置 fileReader.readAsArrayBuffer(file.slice(start, end)); //讀取爲二進制字符串 console.log('loadNext') console.log(end) }; } var repeatcount = 0; //檢查文件是否已經存在 function checkfile(md5value, file) { console.log('checkfile_now') var fd = new FormData(); fd.append('rquesttype', "chekcfile"); fd.append('filename', file.name); fd.append('md5value', md5value); var xhr = new XMLHttpRequest(); xhr.open('post', '../Files/FileUpload', 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) { console.log('secondUpload'); 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 * 2000; //每次讀取文件大小 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', '../Files/FileUpload', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var jsonobj = JSON.parse(xhr.responseText) console.log(jsonobj); console.log(fn); 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', '../Files/FileUpload', 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>
服務器代碼(c# mvc)前端
public ActionResult FileUpload() { string basefilename = HttpContext.Server.MapPath("/FileTemp/"); HttpRequestBase req = HttpContext.Request; string rquesttype = req.Form["rquesttype"]; string result = string.Empty; switch (rquesttype) { case "chekcfile": result = chekcfile(req); break; case "uploadblob": result = uploadblob(req); break; case "finishupload": result = finishupload(req); break; } return Content(result); } /// <summary> /// 結束文件上傳,修改上傳後的名稱 /// </summary> /// <param name="req"></param> public string finishupload(HttpRequestBase req) { string basefilename = HttpContext.Server.MapPath("/FileTemp/"); //接收前端傳遞過來的參數 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; FileInfo okFile = new FileInfo(okname); okFile.Create(); FileInfo oldFile = new FileInfo(oldname); if (oldFile.Exists) { oldFile.Delete(); } FileInfo fullFile = new FileInfo(fullname); fullFile.MoveTo(oldname); var str = string.Format("{{\"data\":\"ok\"}}"); return str; } /// <summary> /// 處理文件分塊上傳的數據 /// </summary> /// <param name="req"></param> public string uploadblob(HttpRequestBase req) { string basefilename = HttpContext.Server.MapPath("/FileTemp/"); long totalCount = 0; if (req.Files.Count <= 0) { return "獲取服務器上傳文件失敗"; } var _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(); //檢查文件已經上傳的大小是否等於文件的總大小 } return "分段數據保存成功"; } /// <summary> /// 檢查文件是否存在 /// </summary> /// <param name="req"></param> public string chekcfile(HttpRequestBase req) { string basefilename = HttpContext.Server.MapPath("/FileTemp/"); 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; FileInfo okFile = new FileInfo(path_ok); FileInfo partFile = new FileInfo(path_part); if (okFile.Exists)//傳完了 { flag = 2; json = string.Format("{{\"flag\":\"{0}\"}}", flag); } else if (partFile.Exists)//傳了一部分 { 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); } return json; }