FileStream实现多线程断点续传(已封装)
生活随笔
收集整理的這篇文章主要介紹了
FileStream实现多线程断点续传(已封装)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
- 處理文件分片
- 處理缺失的分片文件
- 合并分片文件
- MD5驗證文件
?
using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Threading.Tasks; using System.Linq; using System.Text;public class FileTransfer {/// <summary>/// 文件源路徑/// </summary>public string SrcPath { get; private set; }/// <summary>/// 文件的目標路徑/// </summary>public string TgtPath { get; private set; }/// <summary>/// 臨時目錄(存放斷點數據文件)/// </summary>public string TempDir { get; private set; }/// <summary>/// 文件的目標目錄/// </summary>public string TgtDir { get; private set; }/// <summary>/// 數據包大小(默認16mb)/// </summary>public long PackSize { get; set; } = 1024 * 1024 * 16;/// <summary>/// 文件大小/// </summary>public long FileLength { get; private set; }/// <summary>/// 傳輸包大小/// </summary>public int PackCount { get; private set; }/// <summary>/// 斷點續傳/// </summary>/// <param name="srcPath">文件源路徑</param>/// <param name="tgtPath">文件的目標路徑</param>public FileTransfer(string srcPath, string tgtPath): this(srcPath, tgtPath, 1024 * 1024 * 16){}/// <summary>/// 斷點續傳/// </summary>/// <param name="srcPath">文件源路徑</param>/// <param name="tgtPath">文件的目標路徑</param>/// <param name="packSize">數據包大小</param>public FileTransfer(string srcPath, string tgtPath, int packSize){this.SrcPath = srcPath;this.TgtPath = tgtPath;this.PackSize = packSize;FileInfo fileInfo = new FileInfo(this.SrcPath);if (!fileInfo.Exists){throw new ArgumentException("文件不存在!", "srcPath");}this.TgtDir = Path.GetDirectoryName(tgtPath);if (!Directory.Exists(this.TgtDir)){Directory.CreateDirectory(this.TgtDir);}this.FileLength = fileInfo.Length;if ((this.FileLength % this.PackSize) > 0){this.PackCount = (int)(this.FileLength / this.PackSize) + 1;}else{this.PackCount = (int)(this.FileLength / this.PackSize);}this.TempDir = Path.Combine(this.TgtDir, StrMD5(Path.GetFileName(this.TgtPath)));//新new 對象時,刪除臨時文件夾if (Directory.Exists(this.TempDir)){Directory.Delete(this.TempDir, true);}}/// <summary>/// 檢測臨時目錄是否存在,不存在則創建/// </summary>private void CheckTempDir(){if (!Directory.Exists(this.TempDir)){Directory.CreateDirectory(this.TempDir);}}/// <summary>/// md5比對文件/// </summary>/// <returns></returns>public bool Md5Compare(){string md51 = FileMD5(this.SrcPath);string md52 = FileMD5(this.TgtPath);if (md51 == null || md52 == null){return false;}return md51.Equals(md52);}/// <summary>/// 文件分片傳輸/// </summary>public void Transfer(bool isMerge = true){CheckTempDir();//多線程任務var tasks = new Task[this.PackCount];var fy = Task.Factory;for (int index = 0; index < this.PackCount; index++){long Threadindex = index; //這步很關鍵,在Task()里的絕對不能直接使用indexvar task = fy.StartNew(() =>{//臨時文件路徑string tempfilepath = Path.Combine(this.TempDir, GenerateTempName(Threadindex));using (FileStream tempstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write)){int length = (int)Math.Min(PackSize, FileLength - Threadindex * PackSize);var bytes = GetFile(Threadindex * PackSize, length);tempstream.Write(bytes, 0, length);tempstream.Flush();tempstream.Close();tempstream.Dispose();}});tasks[Threadindex] = task;}//等待所有線程完成 Task.WaitAll(tasks);// 合并文件if (isMerge){Merge();}}/// <summary>/// 比對緩存文件,并進行分片/// </summary>public void CompareTransfer(bool isMerge = true){CheckTempDir();//臨時文件夾路徑var tempfiles = new DirectoryInfo(this.TempDir).GetFiles();List<string> Comparefiles = new List<string>();for (int j = 0; j < PackCount; j++){bool hasfile = false;foreach (FileInfo Tempfile in tempfiles){if (Tempfile.Name.Split('_')[1] == j.ToString()){hasfile = true;break;}}if (hasfile == false){Comparefiles.Add(j.ToString());}}//最后補上這些缺失的文件if (Comparefiles.Count > 0){var tasks = new List<Task>();var fy = Task.Factory;foreach (string com_index in Comparefiles){string strIndex = com_index;var task = fy.StartNew(() =>{string tempfilepath = Path.Combine(this.TempDir, GenerateTempName(strIndex));using (FileStream Compstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write)){int length = (int)Math.Min(PackSize, this.FileLength - Convert.ToInt32(strIndex) * this.PackSize);var bytes = GetFile(Convert.ToInt64(strIndex) * PackSize, length);Compstream.Write(bytes, 0, length);Compstream.Flush();Compstream.Close();Compstream.Dispose();}});tasks.Add(task);}//等待所有線程完成 Task.WaitAll(tasks.ToArray());}// 合并文件if (isMerge){Merge();}}/// <summary>/// 合并分片文件/// </summary>/// <param name="isDelTemp">是否刪除臨時文件夾(文件)</param>public void Merge(bool isDelTemp = true){//var tempDirInfo = new DirectoryInfo(this.TempDir);//using (FileStream writestream = new FileStream(this.TgtPath, FileMode.Create, FileAccess.Write, FileShare.Write))//{// var tempfiles = tempDirInfo.GetFiles();// foreach (FileInfo fileInfo in tempfiles)// {// Console.WriteLine(fileInfo.Name);// using (FileStream readTempStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))// {// long onefileLength = fileInfo.Length;// byte[] buffer = new byte[Convert.ToInt32(onefileLength)];// readTempStream.Read(buffer, 0, Convert.ToInt32(onefileLength));// writestream.Write(buffer, 0, Convert.ToInt32(onefileLength));// }// }// writestream.Flush();// writestream.Close();// writestream.Dispose();//}//tempDirInfo.Delete(isDelTemp);var tempDirInfo = new DirectoryInfo(this.TempDir);using (FileStream writestream = new FileStream(this.TgtPath, FileMode.Create, FileAccess.Write, FileShare.Write)){var tempfiles = tempDirInfo.GetFiles();foreach (FileInfo fileInfo in tempfiles){using (FileStream readTempStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){long onefileLength = fileInfo.Length;byte[] buffer = new byte[Convert.ToInt32(onefileLength)];readTempStream.Read(buffer, 0, Convert.ToInt32(onefileLength));writestream.Write(buffer, 0, Convert.ToInt32(onefileLength));}if (isDelTemp){fileInfo.Delete();}}writestream.Flush();writestream.Close();writestream.Dispose();}if (isDelTemp){tempDirInfo.Delete(isDelTemp);}}/// <summary>/// 根據開始位置獲取文件字節流/// </summary>/// <param name="start"></param>/// <param name="length"></param>/// <returns></returns>private byte[] GetFile(long start, int length){using (FileStream ServerStream = new FileStream(this.SrcPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024 * 80, true)){byte[] buffer = new byte[length];ServerStream.Position = start;//ServerStream.Seek(start, SeekOrigin.Begin);ServerStream.Read(buffer, 0, length);return buffer;}}/// <summary>/// 生成臨時文件名稱/// </summary>/// <param name="index"></param>/// <returns></returns>private string GenerateTempName(object index){string res = index.ToString().PadLeft(this.PackCount.ToString().Length, '0') + "_" + this.PackCount;Console.WriteLine(res);return res;}/// <summary>/// 計算文件的Md5/// </summary>/// <param name="path"></param>/// <returns></returns>private static string FileMD5(string path){if (!File.Exists(path)){return null;}int bufferSize = 1024 * 16;byte[] buffer = new byte[bufferSize];Stream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);HashAlgorithm hashAlgorithm = new MD5CryptoServiceProvider();int readLength = 0;//每次讀取長度var output = new byte[bufferSize];while ((readLength = inputStream.Read(buffer, 0, buffer.Length)) > 0){//計算MD5hashAlgorithm.TransformBlock(buffer, 0, readLength, output, 0);}//完成最后計算,必須調用(由于上一部循環已經完成所有運算,所以調用此方法時后面的兩個參數都為0)hashAlgorithm.TransformFinalBlock(buffer, 0, 0);string md5 = BitConverter.ToString(hashAlgorithm.Hash).Replace("-", "");hashAlgorithm.Clear();inputStream.Close();inputStream.Dispose();return md5;}/// <summary>/// 字符串Md5/// </summary>/// <param name="source"></param>/// <returns></returns>private static string StrMD5(string source){byte[] sor = Encoding.UTF8.GetBytes(source);MD5 md5 = MD5.Create();byte[] result = md5.ComputeHash(sor);StringBuilder strbul = new StringBuilder(40);for (int i = 0; i < result.Length; i++){strbul.Append(result[i].ToString("x2"));//加密結果"x2"結果為32位,"x3"結果為48位,"x4"結果為64位 }return strbul.ToString();}}?
轉載于:https://www.cnblogs.com/lztkdr/p/FileTransfer.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的FileStream实现多线程断点续传(已封装)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么样的鼠标对程序员最有用,超级提高开发
- 下一篇: Java异常知识整理_处理异常时的性能开