html5大檔案上傳技術(四)
阿新 • • 發佈:2019-01-26
五、MD5檔案校驗
基於js-spark-md5前端js類庫,可快速獲取檔案Md5值,點選下載:spark-md5.js
一個分片讀取大檔案MD5值得範例(引用)
<body> <div> <div> 新增檔案 <input type="file" name="" id="fileinput"> </div> <progress class='progressbar' value="0" max="100" style='width:500px;margin-top:20px'></progress> <div style='margin-top:20px'> <span id="handler_info" ></span> </div> </div> <script src="Scripts/spark-md5.js"></script> <script> function get_filemd5sum(ofile) { var file = ofile; var tmp_md5; var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, // file = this.files[0], chunkSize = 2 * 1024 * 1024, // Read in chunks of 2MB chunks = Math.ceil(file.size / chunkSize), currentChunk = 0, spark = new SparkMD5.ArrayBuffer(), fileReader = new FileReader(); fileReader.onload = function(e) { // console.log('read chunk nr', currentChunk + 1, 'of', chunks); spark.append(e.target.result); // Append array buffer currentChunk++; var md5_progress = Math.floor((currentChunk / chunks) * 100); console.log(file.name + " 正在處理,請稍等," + "已完成" + md5_progress + "%"); var handler_info = document.getElementById("handler_info"); var progressbar = document.getElementsByClassName("progressbar")[0]; handler_info.innerHTML=file.name + " 正在處理,請稍等," + "已完成" + md5_progress + "%" progressbar.value =md5_progress; if (currentChunk < chunks) { loadNext(); } else { tmp_md5 = spark.end(); console.log(tmp_md5) handler_info.innerHTML = file.name + "的MD5值是:" + tmp_md5; } }; fileReader.onerror = function() { console.warn('oops, something went wrong.'); }; function loadNext() { var start = currentChunk * chunkSize, end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); } loadNext(); } var uploadfile = document.getElementById('fileinput') uploadfile.onchange = function(e){ var file = this.files[0]; if(!file) { alert('請選擇檔案!'); return false; } get_filemd5sum(file) } </script> </body>
通過計算可以獲得檔案唯一的的md5值,因此在做檔案上傳的時候,就只要在前端先獲取要上傳的檔案md5,並把檔案md5傳到伺服器,對比之前檔案的md5,如果存在相同的md5,我們只要把檔案的名字傳到伺服器關聯之前的檔案即可,並不需要再次去上傳相同的檔案,再去耗費儲存資源、上傳的時間、網路頻寬。
範例:客戶端與伺服器端檔案MD5的簡單匹配。
客戶端程式碼:
<body> <div> <div> 新增檔案 <input type="file" name="" id="fileinput"/> <input id="Button1" type="button" onclick ="get_filemd5sum()" value="提交" /></div>
<progress class='progressbar' value="0" max="100" style='width:500px;margin-top:20px'></progress> <div style='margin-top:20px'> <span id="handler_info" ></span> </div> </div> <script src="Scripts/jquery-3.2.1.js"></script> <script src="Scripts/spark-md5.js"></script> <script> function has_file_by_md5(file, fileMD5) { name = file.name; //檔名 size = file.size; //總大小 //構造一個表單,FormData是HTML5新增的 var form = new FormData(); form.append("name", name); form.append("file_md5", fileMD5); form.append("size", size); //檔案大小 //Ajax提交 $.ajax({ url: "File/temp_md5.aspx", type: "POST", data: form, processData: false, //很重要,告訴jquery不要對form進行處理 contentType: false //很重要,指定為false才能形成正確的Content-Type }).done(function (msg, status) { if (msg == "ok") { handler_info.innerHTML+="<br>伺服器端存在此檔案!" } else { handler_info.innerHTML += "<br>伺服器端不存在此檔案!" } }); }; function get_filemd5sum() { var file = document.getElementById('fileinput').files[0]; if (!file) { alert('請選擇檔案!'); return false; } var tmp_md5=""; var shardSize = 2 * 1024 * 1024; //以2MB為一個分片 //獲取檔案MD5的值/*** 大檔案很慢,只讀取第一片****/ var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, spark = new SparkMD5.ArrayBuffer(), fileReader = new FileReader(); var start = 0, end = ((start + shardSize) >= file.size) ? file.size : start + shardSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); fileReader.onload = function (e) { spark.append(e.target.result); // Append array buffer tmp_md5 = spark.end(); var progressbar = document.getElementsByClassName("progressbar")[0]; progressbar.value = 100; //傳送MD5到伺服器 console.log(tmp_md5) has_file_by_md5(file, tmp_md5); handler_info.innerHTML = file.name + "的MD5值是:" + tmp_md5; } fileReader.onerror = function () { console.warn('oops, something went wrong.'); } }; </script> </body>
服務端temp_md5.aspx程式碼:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class temp_md5 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string msg = "";
try
{
//從Request中取引數,注意上傳的檔案在Requst.Files中
string name = Request["name"];
string file_md5 = Request["file_md5"];
long size_total = Convert.ToInt64(Request["size"]);
string dir = Server.MapPath("~/Upload");
string big_file = Path.Combine(dir, name);
string file_name = Path.Combine(dir, name); //檔案
int shardSize = 2 * 1024 * 1024; //以2MB為一個分片
if (System.IO.File.Exists(big_file) == true)//檔案已經上傳
{
FileInfo tempfileinfo = new FileInfo(big_file);//md5相同,檔案大小相同,同一個檔案,秒傳
if (tempfileinfo.Length == size_total)
{
if (file_md5 == get_file_md5(name, dir, shardSize))
{
msg="ok";
}
}
}
}
catch (Exception)
{
msg = "error";
}
Response.Write(msg);
}
public string get_file_md5(string filename, string dir, int shardSize)
{
string msg = "";
try
{
string file_N = Path.Combine(dir, filename);
FileInfo temp_file_info = new FileInfo(file_N);
long start = 0;
long end = Math.Min((long)(temp_file_info.Length), start + shardSize); //大檔案取第一片生成臨時檔案
string temp_file_name = "";
if (temp_file_info.Length > shardSize)
{
temp_file_name = Path.Combine(dir, filename + "_temp");
FileStream file = new FileStream(file_N, System.IO.FileMode.Open);
BinaryReader br = new BinaryReader(file);
byte[] bs = br.ReadBytes(Convert.ToInt32(end));;
br.Close();
file.Close();
FileStream temp_file = new FileStream(temp_file_name, FileMode.Create, FileAccess.Write);
temp_file.Write(bs, 0, bs.Length);
temp_file.Close();
}else
{
temp_file_name = Path.Combine(dir, filename);
}
FileStream file1 = new FileStream(temp_file_name, System.IO.FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file1);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
msg = sb.ToString();
file1.Close();
if (temp_file_info.Length > shardSize)
{
System.IO.File.Delete(temp_file_name);//刪除臨時生成的檔案
}
}
catch (Exception)
{
msg = "error";
}
return (msg);
}
}