• .NET实现多线程拷贝文件


    一、帮助类

    1. using System;
    2. using System.IO;
    3. using System.Threading;
    4. using Microsoft.Win32.SafeHandles;
    5. using System.Runtime.InteropServices;
    6. using System.Diagnostics;
    7. namespace FastCopyClass
    8. {
    9. public class FastCopy
    10. {
    11. private const short FILE_ATTRIBUTE_NORMAL = 0x80;
    12. private const short INVALID_HANDLE_VALUE = -1;
    13. private const uint GENERIC_READ = 0x80000000;
    14. private const uint GENERIC_WRITE = 0x40000000;
    15. private const uint CREATE_NEW = 1;
    16. private const uint CREATE_ALWAYS = 2;
    17. private const uint OPEN_EXISTING = 3;
    18. private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
    19. private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
    20. private const uint FILE_SHARE_READ = 0x00000001;
    21. private const uint FILE_SHARE_WRITE = 0x00000002;
    22. [DllImport("kernel32.dll", SetLastError = true)]
    23. static extern SafeFileHandle CreateFile(string IpFileName, uint dwDesiredAccess,
    24. uint dwShareMode, IntPtr IpSecurityAttributes, uint dwCreationDisposition,
    25. uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    26. private int _ThreadNum;
    27. private Thread[] CopyThread;
    28. private long ReadBufferSize = 1024 * 1024 * 16;
    29. public long TotalReadCount = 0;
    30. public long AverageCopySpeed;
    31. public int ProgressBarValue = 0;
    32. private DateTime start;
    33. private FileInfo SourceFileInfo;
    34. private string _DestFileName;
    35. private string _SourceFileName;
    36. private bool _IsUsingSystemBuff;
    37. public delegate void CopyFinished(string IsFinish);
    38. private bool[] isfirst;
    39. public event CopyFinished CopyF;
    40. private bool WaitingEnd = true;
    41. private DateTime WaitTime;
    42. private int ThreadExitCout = 0;
    43. private object ThreadLock = new object();
    44. /// <summary>
    45. /// 执行复制函数,线程数如果大于8将按照最多8线程复制
    46. /// </summary>
    47. /// <param name="SourceFileName">源文件全路径</param>
    48. /// <param name="DestFileName">目标文件全路径</param>
    49. /// <param name="IsUsingSystemBuff">是否使用系统缓存,不使用系统缓存的复制速度将远大于使用系统缓存的复制速度</param>
    50. /// <param name="ThreadNum">复制线程数</param>
    51. /// <param name="IsSynchronous">true是同步,false是异步</param>
    52. /// <param name="WaitMilliseconds">同步等待时间</param>
    53. public void ExeCopy(string SourceFileName, string DestFileName, bool IsUsingSystemBuff, int ThreadNum, bool IsSynchronous, double WaitMilliseconds)
    54. {
    55. //Console.WriteLine("开始时间:" + DateTime.Now.ToString("hh:mm:ss"));
    56. try
    57. {
    58. SourceFileInfo = new FileInfo(SourceFileName);
    59. _DestFileName = DestFileName;
    60. _SourceFileName = SourceFileName;
    61. _IsUsingSystemBuff = IsUsingSystemBuff;
    62. //if (SourceFileInfo.Exists)
    63. //{
    64. //小文件使用系统复制File.Copy
    65. if (SourceFileInfo.Length > 0 && SourceFileInfo.Length < 100 * 1024 * 1024)
    66. {
    67. File.Copy(SourceFileName, DestFileName,true);
    68. }
    69. else//大于100M文件才使用FastCopy
    70. {
    71. if (ThreadNum > 0)
    72. {
    73. //建立于源文件同样大小的目标空文件
    74. if (initFile(SourceFileName, DestFileName))//如果建立或者覆盖文件成功
    75. {
    76. //打开目标文件
    77. //线程数量限制
    78. ThreadNum = ThreadNum > 8 ? 8 : ThreadNum;
    79. _ThreadNum = ThreadNum;
    80. CopyThread = new Thread[ThreadNum];
    81. isfirst = new bool[ThreadNum];
    82. if (ThreadNum == 1)//执行单线程复制
    83. {
    84. ThreadParams threadParam = new ThreadParams();
    85. threadParam.StartPosition = 0;
    86. threadParam.ReadLength = SourceFileInfo.Length;
    87. threadParam.start = DateTime.Now;
    88. CopyThread[0] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
    89. CopyThread[0].Start(threadParam);
    90. }
    91. else//执行多线程复制
    92. {
    93. long parts = (long)_ThreadNum;
    94. long StartPosition = 0;
    95. long len = SourceFileInfo.Length;
    96. long last = SourceFileInfo.Length % parts;
    97. len = len - last;
    98. long PartLength = len / parts;
    99. PartLength = PartLength - PartLength % 512;
    100. last = SourceFileInfo.Length - parts * PartLength;
    101. start = DateTime.Now;//记录开始时间
    102. for (int i = 0; i < ThreadNum; i++)
    103. {
    104. CopyThread[i] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
    105. CopyThread[i].Name = i.ToString();
    106. if (i == ThreadNum - 1)
    107. {
    108. ThreadParams threadParam = new ThreadParams();
    109. threadParam.StartPosition = StartPosition;
    110. threadParam.ReadLength = PartLength + last;
    111. threadParam.start = start;
    112. CopyThread[i].Start(threadParam);
    113. }
    114. else
    115. {
    116. ThreadParams threadParam = new ThreadParams();
    117. threadParam.StartPosition = StartPosition;
    118. threadParam.ReadLength = PartLength;
    119. StartPosition += PartLength;
    120. threadParam.start = start;
    121. CopyThread[i].Start(threadParam);
    122. }
    123. }
    124. }
    125. }
    126. }
    127. else
    128. throw new Exception("线程数不能小于1");
    129. }
    130. //}
    131. //else
    132. // throw new Exception("打开源文件失败!");
    133. //等待线程结束
    134. if (IsSynchronous)
    135. {
    136. WaitTime = DateTime.Now;
    137. WaitForEnd(WaitMilliseconds);
    138. }
    139. }
    140. catch (Exception ex)
    141. {
    142. PubLibrary.WriteErrLog(ex.ToString());
    143. throw ex;
    144. }
    145. //Console.WriteLine("结束时间:" + DateTime.Now.ToString("hh:mm:ss"));
    146. }
    147. private void WaitForEnd(double WaitMilliseconds)
    148. {
    149. while (ThreadExitCout < _ThreadNum)
    150. {
    151. Thread.Sleep(100);
    152. TimeSpan ts = DateTime.Now.Subtract(WaitTime);
    153. if (ts.TotalMilliseconds > WaitMilliseconds)
    154. {
    155. throw new Exception("文件拷贝超时异常");
    156. break;
    157. }
    158. }
    159. }
    160. private bool initFile(string SourceFileName, string DestFileName)
    161. {
    162. try
    163. {
    164. FileInfo SourceFileInfo = new FileInfo(SourceFileName);
    165. FileInfo DestFileInfo = new FileInfo(DestFileName);
    166. if (DestFileInfo.Exists)
    167. {
    168. DestFileInfo.Delete();
    169. }
    170. Process p = new Process();
    171. p.StartInfo.FileName = "fsutil";
    172. p.StartInfo.Arguments = "file createnew " + DestFileName + " " + SourceFileInfo.Length.ToString();
    173. p.StartInfo.UseShellExecute = false;
    174. p.StartInfo.RedirectStandardOutput = true;
    175. p.StartInfo.RedirectStandardError = true;
    176. p.StartInfo.CreateNoWindow = true;
    177. p.Start();
    178. p.WaitForExit(1000 * 60 * 2);
    179. return true;
    180. }
    181. catch (Exception ex)
    182. {
    183. PubLibrary.WriteErrLog(ex.ToString());
    184. throw ex;
    185. }
    186. }
    187. private void ExeThreadCopy(object obj)
    188. {
    189. ThreadParams param = (ThreadParams)obj;
    190. SafeFileHandle SafeFile_SourceFile = CreateFile(_SourceFileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
    191. OPEN_EXISTING, _IsUsingSystemBuff ? 0 : FILE_FLAG_NO_BUFFERING, IntPtr.Zero);
    192. SafeFileHandle SafeFile_DestFile = CreateFile(_DestFileName, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero,
    193. OPEN_EXISTING, _IsUsingSystemBuff ? 0 : (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH), IntPtr.Zero);
    194. FileStream SourceFileStream = new FileStream(SafeFile_SourceFile, FileAccess.Read);
    195. FileStream DestFileStream = new FileStream(SafeFile_DestFile, FileAccess.Write);
    196. if (param.StartPosition != 0)
    197. {
    198. SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
    199. DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
    200. }
    201. BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
    202. BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);
    203. long ThreadTotalReadCount = 0;
    204. long ThreadOneTimeReadCount = 0;
    205. long ReadCount = 0;
    206. bool IsEndPart = false;
    207. byte[] ReadBuff = new byte[ReadBufferSize];
    208. int ThreadName = int.Parse(Thread.CurrentThread.Name);
    209. while (ThreadTotalReadCount < param.ReadLength)
    210. {
    211. //计算每次应该读取流的长度,因为在每部分的最后一点不一定是ReadBufferSize大小?如果不设置流的读取长度,有可能在每部分最后一次读取越界。读到下一部分的内容。
    212. Console.WriteLine(Thread.CurrentThread.Name);
    213. ReadCount = param.ReadLength - ThreadTotalReadCount < ReadBufferSize ? param.ReadLength - ThreadTotalReadCount : ReadBufferSize;
    214. if (ReadCount % 512 == 0)//不是最后一部分的最后一点
    215. {
    216. IsEndPart = false;
    217. }
    218. else
    219. {
    220. IsEndPart = true;
    221. }
    222. if (IsEndPart)
    223. {
    224. FileStream SourceFileLastStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
    225. FileStream DestFileLastStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
    226. BinaryReader SourceFileReadLast = new BinaryReader(SourceFileLastStream);
    227. BinaryWriter DestFileWriteLast = new BinaryWriter(DestFileLastStream);
    228. SourceFileLastStream.Seek(SourceFileStream.Position, SeekOrigin.Begin);
    229. DestFileLastStream.Seek(DestFileStream.Position, SeekOrigin.Begin);
    230. byte[] LastBuff = new byte[ReadCount];
    231. ThreadOneTimeReadCount = SourceFileReadLast.Read(LastBuff, 0, (int)ReadCount);
    232. DestFileWriteLast.Write(LastBuff, 0, (int)ReadCount);
    233. try
    234. {
    235. SourceFileReadLast.Close();
    236. }
    237. catch { }
    238. try
    239. {
    240. DestFileWriteLast.Close();
    241. }
    242. catch { }
    243. try
    244. {
    245. SourceFileLastStream.Close();
    246. }
    247. catch { }
    248. try
    249. {
    250. DestFileLastStream.Close();
    251. }
    252. catch { }
    253. if (CopyF != null)
    254. {
    255. CopyF("复制完成");
    256. }
    257. }
    258. else
    259. {
    260. ThreadOneTimeReadCount = SourceFileReader.Read(ReadBuff, 0, (int)ReadCount);
    261. DestFileWriter.Write(ReadBuff, 0, (int)ReadCount);
    262. }
    263. TotalReadCount += ThreadOneTimeReadCount;
    264. ThreadTotalReadCount += ThreadOneTimeReadCount;
    265. TimeSpan ts = DateTime.Now.Subtract(param.start);
    266. AverageCopySpeed = TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
    267. ProgressBarValue = (int)(TotalReadCount * 100 / SourceFileInfo.Length);
    268. WaitTime = DateTime.Now;
    269. }
    270. try
    271. {
    272. SourceFileReader.Close();
    273. }
    274. catch { };
    275. try
    276. {
    277. DestFileWriter.Close();
    278. }
    279. catch { };
    280. try
    281. {
    282. SourceFileStream.Close();
    283. }
    284. catch { };
    285. try
    286. {
    287. DestFileStream.Close();
    288. }
    289. catch { };
    290. try
    291. {
    292. SafeFile_SourceFile.Close();
    293. }
    294. catch { };
    295. try
    296. {
    297. SafeFile_DestFile.Close();
    298. }
    299. catch { };
    300. lock (ThreadLock)
    301. {
    302. ThreadExitCout += 1;
    303. }
    304. }
    305. private void ExcNormalCopy(object obj)
    306. {
    307. ThreadParams param = (ThreadParams)obj;
    308. FileStream SourceFileStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
    309. FileStream DestFileStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
    310. BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
    311. BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);
    312. SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
    313. DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
    314. long ThreadTotalReadCount = 0;
    315. long ThreadOneTimeReadCount = 0;
    316. long ReadCount = 0;
    317. byte[] buff = new byte[ReadBufferSize];
    318. while (TotalReadCount < param.ReadLength)
    319. {
    320. ReadCount = param.ReadLength - ThreadTotalReadCount >= ReadBufferSize ? ReadBufferSize : param.ReadLength - ThreadTotalReadCount;
    321. ThreadOneTimeReadCount = SourceFileReader.Read(buff, 0, (int)ReadCount);
    322. DestFileWriter.Write(buff, 0, (int)ReadCount);
    323. TimeSpan ts = DateTime.Now.Subtract(param.start);
    324. TotalReadCount += ThreadOneTimeReadCount;
    325. ThreadTotalReadCount += ThreadOneTimeReadCount;
    326. AverageCopySpeed = TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
    327. ProgressBarValue = (int)(TotalReadCount * 100 / SourceFileInfo.Length);
    328. }
    329. SourceFileReader.Close();
    330. DestFileWriter.Close();
    331. SourceFileStream.Close();
    332. DestFileStream.Close();
    333. }
    334. public void AbortAllThread()
    335. {
    336. for (int i = 0; i < _ThreadNum; i++)
    337. {
    338. if (CopyThread[i].IsAlive)
    339. {
    340. CopyThread[i].Abort();
    341. }
    342. }
    343. }
    344. }
    345. public class ThreadParams
    346. {
    347. public long StartPosition;
    348. public long ReadLength;
    349. public DateTime start;
    350. }
    351. }

    二、使用

    1. using System;
    2. using FastCopyClass;
    3. namespace FileUploadClass
    4. {
    5. public class FileUpload
    6. {
    7. private static FastCopy fc = new FastCopy();
    8. /// <summary>
    9. /// 复制文件夹及文件
    10. /// </summary>
    11. /// <param name="sourceFolder">原文件路径</param>
    12. /// <param name="destFolder">目标文件路径</param>
    13. /// <returns></returns>
    14. public static bool CopyFolder(string sourceFolder, string destFolder)
    15. {
    16. try
    17. {
    18. PubLibrary.WriteErrLog("复制文件开始:" + DateTime.Now.ToString("yyyy-MM/dd HH:mm:ss"));
    19. string folderName = System.IO.Path.GetFileName(sourceFolder);
    20. string destfolderdir = System.IO.Path.Combine(destFolder, folderName);
    21. string[] filenames = System.IO.Directory.GetFileSystemEntries(sourceFolder);
    22. foreach (string file in filenames)// 遍历所有的文件和目录
    23. {
    24. if (System.IO.Directory.Exists(file))
    25. {
    26. string currentdir = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
    27. if (!System.IO.Directory.Exists(currentdir))
    28. {
    29. System.IO.Directory.CreateDirectory(currentdir);
    30. }
    31. CopyFolder(file, destfolderdir);
    32. }
    33. else
    34. {
    35. string srcfileName = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
    36. if (!System.IO.Directory.Exists(destfolderdir))
    37. {
    38. System.IO.Directory.CreateDirectory(destfolderdir);
    39. }
    40. fc.ExeCopy(file,srcfileName,false,8,false,30 * 60 * 60 * 1000);
    41. //System.IO.File.Copy(file, srcfileName,true);
    42. }
    43. }
    44. PubLibrary.WriteErrLog("复制文件结束:" + DateTime.Now.ToString("yyyy-MM/dd HH:mm:ss"));
    45. return true;
    46. }
    47. catch (Exception ex)
    48. {
    49. PubLibrary.WriteErrLog("复制粘贴文件夹:" + ex.ToString());
    50. return false;
    51. }
    52. }
    53. }
    54. }

  • 相关阅读:
    ant-design中form组件的item在typescript环境下自定义提示文字(Form.Item的tooltip属性)及提示图标
    部署支持使用Redis哨兵模式,支持纳管ClickHouse数据库,JumpServer堡垒机v2.28.0发布
    PLC可以连接哪些工业设备实现远距离无线通讯?工业网关可以吗?
    稳了,我用 Python 可视化分析和预测了 2022 年 FIFA 世界杯
    Spring Boot 入门笔记
    如何设置和取消ZIP文件的自动加密
    [附源码]Python计算机毕业设计Django基于JavaWeb的学校社团活动管理系统
    怎样才能写好项目文档?
    cocos入门3:新建项目
    高级时钟项目(2)Json文件解析学习---C语言版本
  • 原文地址:https://blog.csdn.net/weixin_50478033/article/details/133272211