.NET实现多线程拷贝文件

一、帮助类

using System;
using System.IO;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace FastCopyClass
{
    public class FastCopy
    {
        private const short FILE_ATTRIBUTE_NORMAL = 0x80;
        private const short INVALID_HANDLE_VALUE = -1;
        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;
        private const uint CREATE_NEW = 1;
        private const uint CREATE_ALWAYS = 2;
        private const uint OPEN_EXISTING = 3;
        private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
        private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
        private const uint FILE_SHARE_READ = 0x00000001;
        private const uint FILE_SHARE_WRITE = 0x00000002;
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern SafeFileHandle CreateFile(string IpFileName, uint dwDesiredAccess,
            uint dwShareMode, IntPtr IpSecurityAttributes, uint dwCreationDisposition,
            uint dwFlagsAndAttributes, IntPtr hTemplateFile);
        private int _ThreadNum;
        private Thread[] CopyThread;
        private long ReadBufferSize = 1024 * 1024 * 16;
        public long TotalReadCount = 0;
        public long AverageCopySpeed;
        public int ProgressBarValue = 0;
        private DateTime start;
        private FileInfo SourceFileInfo;
        private string _DestFileName;
        private string _SourceFileName;
        private bool _IsUsingSystemBuff;
        public delegate void CopyFinished(string IsFinish);
        private bool[] isfirst;
        public event CopyFinished CopyF;
        private bool WaitingEnd = true;
        private DateTime WaitTime;
        private int ThreadExitCout = 0;
        private object ThreadLock = new object();

        /// <summary>
        /// 执行复制函数,线程数如果大于8将按照最多8线程复制
        /// </summary>
        /// <param name="SourceFileName">源文件全路径</param>
        /// <param name="DestFileName">目标文件全路径</param>
        /// <param name="IsUsingSystemBuff">是否使用系统缓存,不使用系统缓存的复制速度将远大于使用系统缓存的复制速度</param>
        /// <param name="ThreadNum">复制线程数</param>
        /// <param name="IsSynchronous">true是同步,false是异步</param>
        /// <param name="WaitMilliseconds">同步等待时间</param>
        public void ExeCopy(string SourceFileName, string DestFileName, bool IsUsingSystemBuff, int ThreadNum, bool IsSynchronous, double WaitMilliseconds)
        {
            //Console.WriteLine("开始时间:" + DateTime.Now.ToString("hh:mm:ss"));
            try
            {
                SourceFileInfo = new FileInfo(SourceFileName);
                _DestFileName = DestFileName;
                _SourceFileName = SourceFileName;
                _IsUsingSystemBuff = IsUsingSystemBuff;
                //if (SourceFileInfo.Exists)
                //{
                //小文件使用系统复制File.Copy
                if (SourceFileInfo.Length > 0 && SourceFileInfo.Length < 100 * 1024 * 1024)
                {
                    File.Copy(SourceFileName, DestFileName,true);
                }
                else//大于100M文件才使用FastCopy
                {
                    if (ThreadNum > 0)
                    {
                        //建立于源文件同样大小的目标空文件
                        if (initFile(SourceFileName, DestFileName))//如果建立或者覆盖文件成功
                        {
                            //打开目标文件

                            //线程数量限制
                            ThreadNum = ThreadNum > 8 ? 8 : ThreadNum;
                            _ThreadNum = ThreadNum;
                            CopyThread = new Thread[ThreadNum];
                            isfirst = new bool[ThreadNum];
                            if (ThreadNum == 1)//执行单线程复制
                            {
                                ThreadParams threadParam = new ThreadParams();
                                threadParam.StartPosition = 0;
                                threadParam.ReadLength = SourceFileInfo.Length;
                                threadParam.start = DateTime.Now;
                                CopyThread[0] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
                                CopyThread[0].Start(threadParam);
                            }
                            else//执行多线程复制
                            {
                                long parts = (long)_ThreadNum;
                                long StartPosition = 0;
                                long len = SourceFileInfo.Length;
                                long last = SourceFileInfo.Length % parts;
                                len = len - last;
                                long PartLength = len / parts;
                                PartLength = PartLength - PartLength % 512;
                                last = SourceFileInfo.Length - parts * PartLength;
                                start = DateTime.Now;//记录开始时间
                                for (int i = 0; i < ThreadNum; i++)
                                {
                                    CopyThread[i] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
                                    CopyThread[i].Name = i.ToString();
                                    if (i == ThreadNum - 1)
                                    {
                                        ThreadParams threadParam = new ThreadParams();
                                        threadParam.StartPosition = StartPosition;
                                        threadParam.ReadLength = PartLength + last;
                                        threadParam.start = start;
                                        CopyThread[i].Start(threadParam);
                                    }
                                    else
                                    {
                                        ThreadParams threadParam = new ThreadParams();
                                        threadParam.StartPosition = StartPosition;
                                        threadParam.ReadLength = PartLength;
                                        StartPosition += PartLength;
                                        threadParam.start = start;
                                        CopyThread[i].Start(threadParam);
                                    }
                                }
                            }

                        }
                    }
                    else
                        throw new Exception("线程数不能小于1");
                }
                //}
                //else
                //    throw new Exception("打开源文件失败!");
                //等待线程结束
                if (IsSynchronous)
                {
                    WaitTime = DateTime.Now;
                    WaitForEnd(WaitMilliseconds);
                }
            }
            catch (Exception ex)
            {
                PubLibrary.WriteErrLog(ex.ToString());
                throw ex;
            }
            //Console.WriteLine("结束时间:" + DateTime.Now.ToString("hh:mm:ss"));
        }

        private void WaitForEnd(double WaitMilliseconds)
        {
            while (ThreadExitCout < _ThreadNum)
            {
                Thread.Sleep(100);
                TimeSpan ts = DateTime.Now.Subtract(WaitTime);
                if (ts.TotalMilliseconds > WaitMilliseconds)
                {
                    throw new Exception("文件拷贝超时异常");
                    break;
                }
            }
        }

        private bool initFile(string SourceFileName, string DestFileName)
        {
            try
            {
                FileInfo SourceFileInfo = new FileInfo(SourceFileName);
                FileInfo DestFileInfo = new FileInfo(DestFileName);

                if (DestFileInfo.Exists)
                {
                    DestFileInfo.Delete();
                }
                Process p = new Process();
                p.StartInfo.FileName = "fsutil";
                p.StartInfo.Arguments = "file createnew " + DestFileName + " " + SourceFileInfo.Length.ToString();
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = true;
                p.Start();
                p.WaitForExit(1000 * 60 * 2);
                return true;
            }
            catch (Exception ex)
            {
                PubLibrary.WriteErrLog(ex.ToString());
                throw ex;
            }
        }

        private void ExeThreadCopy(object obj)
        {
            ThreadParams param = (ThreadParams)obj;

            SafeFileHandle SafeFile_SourceFile = CreateFile(_SourceFileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
                                                            OPEN_EXISTING, _IsUsingSystemBuff ? 0 : FILE_FLAG_NO_BUFFERING, IntPtr.Zero);
            SafeFileHandle SafeFile_DestFile = CreateFile(_DestFileName, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero,
                                                          OPEN_EXISTING, _IsUsingSystemBuff ? 0 : (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH), IntPtr.Zero);

            FileStream SourceFileStream = new FileStream(SafeFile_SourceFile, FileAccess.Read);
            FileStream DestFileStream = new FileStream(SafeFile_DestFile, FileAccess.Write);
            if (param.StartPosition != 0)
            {
                SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
                DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            }
            BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
            BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);

            long ThreadTotalReadCount = 0;
            long ThreadOneTimeReadCount = 0;
            long ReadCount = 0;
            bool IsEndPart = false;
            byte[] ReadBuff = new byte[ReadBufferSize];
            int ThreadName = int.Parse(Thread.CurrentThread.Name);
            while (ThreadTotalReadCount < param.ReadLength)
            {
                //计算每次应该读取流的长度,因为在每部分的最后一点不一定是ReadBufferSize大小?如果不设置流的读取长度,有可能在每部分最后一次读取越界。读到下一部分的内容。
                Console.WriteLine(Thread.CurrentThread.Name);
                ReadCount = param.ReadLength - ThreadTotalReadCount < ReadBufferSize ? param.ReadLength - ThreadTotalReadCount : ReadBufferSize;
                if (ReadCount % 512 == 0)//不是最后一部分的最后一点
                {
                    IsEndPart = false;
                }
                else
                {
                    IsEndPart = true;
                }

                if (IsEndPart)
                {
                    FileStream SourceFileLastStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                    FileStream DestFileLastStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
                    BinaryReader SourceFileReadLast = new BinaryReader(SourceFileLastStream);
                    BinaryWriter DestFileWriteLast = new BinaryWriter(DestFileLastStream);
                    SourceFileLastStream.Seek(SourceFileStream.Position, SeekOrigin.Begin);
                    DestFileLastStream.Seek(DestFileStream.Position, SeekOrigin.Begin);
                    byte[] LastBuff = new byte[ReadCount];
                    ThreadOneTimeReadCount = SourceFileReadLast.Read(LastBuff, 0, (int)ReadCount);
                    DestFileWriteLast.Write(LastBuff, 0, (int)ReadCount);
                    try
                    {
                        SourceFileReadLast.Close();
                    }
                    catch { }
                    try
                    {
                        DestFileWriteLast.Close();
                    }
                    catch { }
                    try
                    {
                        SourceFileLastStream.Close();
                    }
                    catch { }
                    try
                    {
                        DestFileLastStream.Close();
                    }
                    catch { }
                    if (CopyF != null)
                    {
                        CopyF("复制完成");
                    }
                }
                else
                {
                    ThreadOneTimeReadCount = SourceFileReader.Read(ReadBuff, 0, (int)ReadCount);
                    DestFileWriter.Write(ReadBuff, 0, (int)ReadCount);
                }
                TotalReadCount += ThreadOneTimeReadCount;
                ThreadTotalReadCount += ThreadOneTimeReadCount;
                TimeSpan ts = DateTime.Now.Subtract(param.start);
                AverageCopySpeed = TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
                ProgressBarValue = (int)(TotalReadCount * 100 / SourceFileInfo.Length);
                WaitTime = DateTime.Now;
            }
            try
            {
                SourceFileReader.Close();
            }
            catch { };
            try
            {
                DestFileWriter.Close();
            }
            catch { };
            try
            {
                SourceFileStream.Close();
            }
            catch { };
            try
            {
                DestFileStream.Close();
            }
            catch { };
            try
            {
                SafeFile_SourceFile.Close();
            }
            catch { };
            try
            {
                SafeFile_DestFile.Close();
            }
            catch { };
            lock (ThreadLock)
            {
                ThreadExitCout += 1;
            }
        }

        private void ExcNormalCopy(object obj)
        {
            ThreadParams param = (ThreadParams)obj;
            FileStream SourceFileStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            FileStream DestFileStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
            BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
            BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);
            SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            long ThreadTotalReadCount = 0;
            long ThreadOneTimeReadCount = 0;
            long ReadCount = 0;
            byte[] buff = new byte[ReadBufferSize];
            while (TotalReadCount < param.ReadLength)
            {
                ReadCount = param.ReadLength - ThreadTotalReadCount >= ReadBufferSize ? ReadBufferSize : param.ReadLength - ThreadTotalReadCount;
                ThreadOneTimeReadCount = SourceFileReader.Read(buff, 0, (int)ReadCount);
                DestFileWriter.Write(buff, 0, (int)ReadCount);
                TimeSpan ts = DateTime.Now.Subtract(param.start);
                TotalReadCount += ThreadOneTimeReadCount;
                ThreadTotalReadCount += ThreadOneTimeReadCount;
                AverageCopySpeed = TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
                ProgressBarValue = (int)(TotalReadCount * 100 / SourceFileInfo.Length);
            }
            SourceFileReader.Close();
            DestFileWriter.Close();
            SourceFileStream.Close();
            DestFileStream.Close();
        }

        public void AbortAllThread()
        {
            for (int i = 0; i < _ThreadNum; i++)
            {
                if (CopyThread[i].IsAlive)
                {
                    CopyThread[i].Abort();
                }
            }
        }
    }

    public class ThreadParams
    {
        public long StartPosition;
        public long ReadLength;
        public DateTime start;
    }
}

二、使用

using System;
using FastCopyClass;

namespace FileUploadClass
{
    public class FileUpload
    {
        private static FastCopy fc = new FastCopy();
        /// <summary>
        /// 复制文件夹及文件
        /// </summary>
        /// <param name="sourceFolder">原文件路径</param>
        /// <param name="destFolder">目标文件路径</param>
        /// <returns></returns>
        public static bool CopyFolder(string sourceFolder, string destFolder)
        {
            try
            {
                PubLibrary.WriteErrLog("复制文件开始:" + DateTime.Now.ToString("yyyy-MM/dd HH:mm:ss"));
                string folderName = System.IO.Path.GetFileName(sourceFolder);
                string destfolderdir = System.IO.Path.Combine(destFolder, folderName);
                string[] filenames = System.IO.Directory.GetFileSystemEntries(sourceFolder);
                foreach (string file in filenames)// 遍历所有的文件和目录
                {
                    if (System.IO.Directory.Exists(file))
                    {
                        string currentdir = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
                        if (!System.IO.Directory.Exists(currentdir))
                        {
                            System.IO.Directory.CreateDirectory(currentdir);
                        }
                        CopyFolder(file, destfolderdir);
                    }
                    else
                    {
                        string srcfileName = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
                        if (!System.IO.Directory.Exists(destfolderdir))
                        {
                            System.IO.Directory.CreateDirectory(destfolderdir);
                        }
                        fc.ExeCopy(file,srcfileName,false,8,false,30 * 60 * 60 * 1000);
                        //System.IO.File.Copy(file, srcfileName,true);
                    }
                }
                PubLibrary.WriteErrLog("复制文件结束:" + DateTime.Now.ToString("yyyy-MM/dd HH:mm:ss"));

                return true;
            }
            catch (Exception ex)
            {
                PubLibrary.WriteErrLog("复制粘贴文件夹:" + ex.ToString());
                return false;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_50478033/article/details/133272211