• 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()里的绝对不能直接使用index
                var 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)
            {
                //计算MD5
                hashAlgorithm.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();
        }
    
    }
  • 相关阅读:
    与大神聊天1h
    《海上钢琴师》观后感
    《小王子》读书笔记
    Joining Data with dplyr in R
    SQL学习笔记1
    电影《受益人》观后感
    markdown文本编辑学习笔记2
    importing-cleaning-data-in-r-case-studies
    一个测试人员的工作该怎么开展
    测试总结报告
  • 原文地址:https://www.cnblogs.com/lztkdr/p/FileTransfer.html
Copyright © 2020-2023  润新知