C# FTP上传(支持断点续传)
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
当前博文只支持上传功能,已经过测试,可以直接使用,但请注意你的使用场景,本人FTP服务端使用 FileZilla Server。
日志记录请看君修改成自己的。初始时修改自己的文件目录。本文上传文件采用队列形式。废话就不多说了,直接上代码。 1. public class LoadFileEventArgs : CancelEventArgs 2. { 3. /// <summary>Ftp文件</summary> 4. public String Src { get; set; } 5. 6. /// <summary>Ftp文件目录</summary> 7. public String SrcDir { get; set; } 8. 9. /// <summary>源文件大小</summary> 10. public Int64 SrcSize { get; set; } 11. 12. /// <summary>目标文件</summary> 13. public String Des { get; set; } 14. 15. /// <summary>目标文件大小</summary> 16. public Int64 DesSize { get; set; } 17. 18. /// <summary>断点续传</summary> 19. public bool FileAppend { get; set; } 20. 21. /// <summary>该文件需要压缩</summary> 22. //public Boolean NeedZipFile { get; set; } 23. 24. /// <summary>该文件太大需要分段压缩</summary> 25. //public Boolean NeedSubZipFile { get; set; } 26. } 1. public class FtpHelper 2. { 3. public String ftpHostName; 4. public String ftpUserId; 5. public String ftpPassword; 6. public String fileParam; // ../Datas/Robot-{参数}/ 7. 8. private static readonly object sys = new object(); 9. private static FtpHelper instance = null; 10. private Queue<LoadFileEventArgs> pathQueue = new Queue<LoadFileEventArgs>(); 11. 12. private String ftpURI { 13. get { return String.Format("ftp://{0}", ftpHostName); } 14. } 15. private String localDir { 16. get { return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Datas\\"); } 17. } 18. private String remoteDir { 19. get { return String.Format("Robot-{0}//", fileParam); } 20. } 21. 22. public static FtpHelper Instance 23. { 24. get 25. { 26. if (instance == null){ 27. lock (sys){ 28. if (instance == null) 29. instance = new FtpHelper(); 30. } 31. } 32. 33. return instance; 34. } 35. } 36. private delegate bool AsyncDelegate(LoadFileEventArgs arg); 37. private event EventHandler<LoadFileEventArgs> onUploadFileEvent; 38. private event EventHandler<LoadFileEventArgs> onUploadFileFinishedEvent; 39. public event EventHandler onMonitorUploadEvent; 40. private bool IsWorking = false;//上传工作是否在进行; 41. private FtpWebResponse webresp = null; 42. 43. 44. private FtpHelper() { 45. onUploadFileEvent += FtpHelper_onUploadFileEvent; 46. onUploadFileFinishedEvent += FtpHelper_onUploadFileFinishedEvent; 47. onMonitorUploadEvent += FtpHelper_onMonitorUploadEvent; 48. this.onMonitorUploadEvent?.Invoke(null, null); //测试时注释 49. } 50. 51. /// <summary> 52. /// 测试接口,正式发布时将要注释 53. /// </summary> 54. public void FuncInit() { 55. 56. //UploadDirectory(); 57. } 58. 59. /// <summary> 60. /// 上传目录下文件 61. /// </summary> 62. private void UploadDirectory() { 63. if (!System.IO.Directory.Exists(localDir)) return; 64. String[] fileArray; 65. String[] pathArray = System.IO.Directory.GetDirectories(localDir); 66. String strDir = String.Empty; 67. foreach (String str in pathArray) { 68. fileArray = System.IO.Directory.GetFiles(str); 69. foreach (String temp in fileArray) { 70. InsertQueue(temp, str); 71. } 72. } 73. 74. //准备上传文件 75. PerUpload(); 76. 77. } 78. 79. /// <summary> 80. /// 插入队列 81. /// </summary> 82. /// <param name="filePath"></param> 83. /// <param name="dirPath"></param> 84. void InsertQueue(String filePath, String dirPath = "") { 85. if (!System.IO.File.Exists(filePath)) return; 86. if (String.IsNullOrEmpty(dirPath)){ 87. String[] pathArray = System.IO.Directory.GetDirectories(localDir); 88. foreach (String str in pathArray){ 89. if (filePath.Contains(str)){ 90. dirPath = str; 91. break; 92. } 93. } 94. } 95. 96. System.IO.FileInfo fi = new System.IO.FileInfo(filePath); 97. String strDir = String.Format("{0}{1}//{2}//{3}//{4}", remoteDir, dirPath.Split('\\').Last(), fi.CreationTime.Date.Year, fi.CreationTime.Date.Month, fi.CreationTime.Date.Day); 98. this.pathQueue.Enqueue(new LoadFileEventArgs{ 99. Des = filePath, 100. SrcDir = String.Format("ftp://{0}/{1}", ftpHostName, strDir), 101. Src = String.Format("ftp://{0}//{1}/{2}", ftpHostName, strDir, fi.Name), 102. DesSize = fi.Length, 103. SrcSize = 0, 104. FileAppend = false 105. }); 106. } 107. 108. private void PerUpload() { 109. Task.Factory.StartNew(() => { 110. if (FTPConnonect()){ 111. try 112. { 113. IsWorking = true; 114. AsyncDelegate uploadDelegate; 115. while (this.pathQueue != null && this.pathQueue.Count > 0){ 116. if (!FTPConnonect()) 117. break; 118. 119. System.Threading.Thread.Sleep(100); 120. LoadFileEventArgs args = this.pathQueue.Peek(); 121. //检查上传文件所在的目录是否存在,不存在则创建所在的目录。 122. MakeDir(args.SrcDir); 123. //检查文件是否已存在并判断文件大小 124. Boolean res = HandelMatchFile(args); 125. if (!res){ 126. //FTP服务器上文件已存在,删除准备出栈的队列。 127. this.pathQueue.Dequeue(); 128. continue; 129. } 130. 131. uploadDelegate = new AsyncDelegate(instance.Upload); 132. IAsyncResult iasync = uploadDelegate.BeginInvoke(args, new AsyncCallback(FuncCallBack), null); 133. bool result = uploadDelegate.EndInvoke(iasync); 134. if (result){ 135. this.onUploadFileFinishedEvent?.Invoke(null, args); 136. } 137. else 138. this.pathQueue.Dequeue(); 139. } 140. } 141. catch (System.Exception ex){ 142. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("文件上传FTP服务器端异常:{0}", ex.ToString()), MessageType = LogType.Info }); 143. } 144. finally { 145. IsWorking = false; 146. } 147. } 148. }); 149. } 150. 151. private bool FTPConnonect(){ 152. DateTime currTime = DateTime.Now; 153. int timed = 300; //约定定时时长300s=5分钟。 154. 155. while (true){ 156. try 157. { 158. //超过约定时长,退出循环 159. if (DateTime.Now.Subtract(currTime).Seconds > timed) 160. return false; 161. 162. using (webresp = (FtpWebResponse)SetFtpWebRequest(ftpURI, WebRequestMethods.Ftp.ListDirectory).GetResponse()){ 163. webresp.Dispose(); 164. webresp.Close(); 165. 166. return true; 167. } 168. } 169. catch (Exception ex){ 170. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("FTP服务器端连接失败,异常:{0}", ex.ToString()), MessageType = LogType.Info }); 171. if (webresp != null){ 172. webresp.Dispose(); 173. webresp.Close(); 174. } 175. 176. System.Threading.Thread.Sleep(10000); 177. } 178. } 179. } 180. 181. /// <summary> 182. /// 文件上传 183. /// </summary> 184. /// <param name="arg"></param> 185. /// <returns></returns> 186. private bool Upload(LoadFileEventArgs arg) { 187. Stream reqStream = null; 188. FileStream fs = null; 189. FtpWebResponse uploadResponse = null; 190. 191. Uri uri = new Uri(arg.Src); 192. FtpWebRequest reqFtp = (FtpWebRequest)WebRequest.Create(uri); 193. reqFtp.Method = WebRequestMethods.Ftp.UploadFile; 194. reqFtp.KeepAlive = false; 195. reqFtp.Credentials = new NetworkCredential(ftpUserId, ftpPassword); 196. reqFtp.UsePassive = false; 197. reqFtp.UseBinary = true; 198. reqFtp.ContentLength = arg.DesSize; 199. reqFtp.Timeout = 10000; 200. fs = System.IO.File.Open(arg.Des, FileMode.Open); 201. byte[] buffer = new byte[2024]; 202. int bytesRead; 203. 204. try 205. { 206. if (arg.FileAppend){ 207. //断点续传 208. reqFtp.Method = WebRequestMethods.Ftp.AppendFile; 209. reqFtp.ContentOffset = arg.SrcSize; 210. //fs.Position = arg.SrcSize; 211. fs.Seek(arg.SrcSize, 0); 212. } 213. 214. using (reqStream = reqFtp.GetRequestStream()){ 215. while (true){ 216. bytesRead = fs.Read(buffer, 0, buffer.Length); 217. if (bytesRead == 0) 218. break; 219. 220. reqStream.Write(buffer, 0, bytesRead); 221. } 222. 223. reqStream.Dispose(); 224. reqStream.Close(); 225. } 226. 227. uploadResponse = reqFtp.GetResponse() as FtpWebResponse; 228. return true; 229. } 230. catch (Exception ex){ 231. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("上传文件到ftp服务器出错:{0}", ex.ToString()), MessageType = LogType.Info }); 232. return false; 233. } 234. finally{ 235. if (reqStream != null){ 236. reqStream.Dispose(); 237. reqStream.Close(); 238. } 239. 240. if (fs != null){ 241. fs.Dispose(); 242. fs.Close(); 243. } 244. 245. if (uploadResponse != null){ 246. uploadResponse.Dispose(); 247. uploadResponse.Close(); 248. } 249. } 250. } 251. 252. /// <summary> 253. /// 回调函数,暂时保留 254. /// </summary> 255. /// <param name="res"></param> 256. void FuncCallBack(IAsyncResult res) { 257. if (res.IsCompleted) 258. { 259. //暂时保留 260. } 261. } 262. 263. /// <summary> 264. /// FTP服务器端创建文件夹。 265. /// </summary> 266. /// <param name="strPath"></param> 267. private void MakeDir(String strPath) { 268. try{ 269. if (RemoteDirExist(strPath)) return; 270. using (webresp = (FtpWebResponse)SetFtpWebRequest(strPath, WebRequestMethods.Ftp.MakeDirectory).GetResponse()){ 271. using (Stream ftpStream = webresp.GetResponseStream()){ 272. ftpStream.Dispose(); 273. ftpStream.Close(); 274. webresp.Dispose(); 275. webresp.Close(); 276. } 277. } 278. } 279. catch (Exception ex){ 280. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("FTP服务器端创建文件夹异常:{0}", ex.ToString()), MessageType = LogType.Info }); 281. } 282. } 283. 284. /// <summary> 285. /// 检查FTP服务端所匹配的文件(服务器断网、客户端断网等多种因素造成) 286. /// </summary> 287. private Boolean HandelMatchFile(LoadFileEventArgs args) { 288. Boolean res = true; 289. Tuple<int, int> tuple = CompareFileSize(args); 290. switch (tuple.Item1) { 291. case 1: 292. break; 293. case 2: 294. //尝试断点续传服务端文件 295. args.FileAppend = true; 296. args.SrcSize = tuple.Item2; 297. res = true; 298. break; 299. case 3: 300. try 301. { 302. //删除本地文件 303. System.IO.File.Delete(args.Des); 304. } 305. catch (System.Exception ex){ 306. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("###删除本地文件失败,异常原因:{0}", ex.Message), MessageType = LogType.Error }); 307. } 308. res = false; 309. break; 310. default: 311. res = false; 312. break; 313. } 314. 315. return res; 316. } 317. 318. /// <summary> 319. /// 检查FTP服务端是否存在当前目录 320. /// </summary> 321. /// <param name="remoteDirName"></param> 322. /// <returns></returns> 323. private bool RemoteDirExist(String remoteDirName) { 324. try{ 325. using (webresp = (FtpWebResponse)SetFtpWebRequest(remoteDirName, WebRequestMethods.Ftp.ListDirectoryDetails).GetResponse()) { 326. webresp.Dispose(); 327. webresp.Close(); 328. return true; 329. } 330. } 331. catch (System.Exception ex){ 332. if (webresp != null){ 333. webresp.Dispose(); 334. webresp.Close(); 335. } 336. 337. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("检查FTP服务器端目录文件造成异常:{0}", ex.ToString()), MessageType = LogType.Info }); 338. return false; 339. } 340. } 341. 342. /// <summary> 343. /// 获取FTP服务端文件基本信息(文件名、大小),文件不存在返回空 344. /// </summary> 345. /// <param name="arg"></param> 346. /// <returns></returns> 347. private String GetRemoteFileDetails(String uri) { 348. StreamReader sr = null; 349. String content = String.Empty; 350. try 351. { 352. using (webresp = (FtpWebResponse)SetFtpWebRequest(uri, WebRequestMethods.Ftp.ListDirectoryDetails).GetResponse()){ 353. using (sr = new StreamReader(webresp.GetResponseStream(), Encoding.Default)){ 354. content = sr.ReadLine(); 355. 356. sr.Dispose(); 357. sr.Close(); 358. webresp.Dispose(); 359. webresp.Close(); 360. 361. return content; 362. } 363. } 364. } 365. catch(System.Exception ex){ 366. if (sr != null){ 367. sr.Dispose(); 368. sr.Close(); 369. } 370. 371. if (webresp != null){ 372. webresp.Dispose(); 373. webresp.Close(); 374. } 375. 376. //SaveLog.WriteLog(new LogObj() { MessageText = String.Format("获取FTP服务器端文件信息时产生异常:{0}", ex.ToString()), MessageType = LogType.Info }); 377. return content; 378. } 379. } 380. 381. /// <summary> 382. /// 删除FTP服务端文件 383. /// </summary> 384. /// <param name="uri"></param> 385. private void DelRemoteFile(String uri) 386. { 387. StreamReader sr = null; 388. String content = String.Empty; 389. try 390. { 391. using (webresp = (FtpWebResponse)SetFtpWebRequest(uri, WebRequestMethods.Ftp.DeleteFile).GetResponse()){ 392. using (sr = new StreamReader(webresp.GetResponseStream(), Encoding.Default)){ 393. content = sr.ReadToEnd(); 394. 395. sr.Dispose(); 396. sr.Close(); 397. webresp.Dispose(); 398. webresp.Close(); 399. } 400. } 401. } 402. catch 403. { 404. if (sr != null){ 405. sr.Dispose(); 406. sr.Close(); 407. } 408. 409. if (webresp != null){ 410. webresp.Dispose(); 411. webresp.Close(); 412. } 413. } 414. } 415. 416. /// <summary> 417. /// 设置FTP WebRequest参数 418. /// </summary> 419. /// <param name="uri"></param> 420. /// <param name="webRequestMethods"></param> 421. /// <param name="timeOut"></param> 422. /// <returns></returns> 423. FtpWebRequest SetFtpWebRequest(String uri, String webRequestMethods, int timeOut=3000) { 424. FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(new Uri(uri)); 425. ftpRequest.Method = webRequestMethods; 426. ftpRequest.UsePassive = false; 427. ftpRequest.UseBinary = true; 428. ftpRequest.KeepAlive = false; 429. ftpRequest.Timeout = timeOut; 430. ftpRequest.Credentials = new NetworkCredential(ftpUserId, ftpPassword); 431. return ftpRequest; 432. } 433. 434. /// <summary> 435. /// 对比远程文件大小 436. /// 1. 远程无此文件返回 --- 1 437. /// 2. 远程文件比本地小 --- 2 438. /// 3. 远程文件与本地大小一致 --- 3 439. /// </summary> 440. /// <param name="args"></param> 441. /// <returns></returns> 442. private Tuple<int, int> CompareFileSize(LoadFileEventArgs args) { 443. String content = GetRemoteFileDetails(args.Src); 444. if (String.IsNullOrEmpty(content)) 445. return new Tuple<int, int>(1, 0); 446. 447. //匹配已上传文件的大小 448. Regex regex = new Regex(@"^-.+?\sftp.+?\s+(?<size>\d+)", RegexOptions.IgnoreCase); 449. Match match = regex.Match(content); 450. if (match.Success) { 451. int fileSize = Convert.ToInt32(match.Groups["size"].Value); 452. if (fileSize < args.DesSize) 453. return new Tuple<int, int>(2, fileSize); 454. 455. return new Tuple<int, int>(3, fileSize); 456. } 457. 458. return new Tuple<int, int>(2, 0); 459. } 460. 461. /// <summary> 462. /// 获取FTP服务器端文件大小 463. /// </summary> 464. /// <param name="src"></param> 465. /// <returns></returns> 466. long GetRemoteFileSize(String src) { 467. long size; 468. try 469. { 470. using (webresp = (FtpWebResponse)SetFtpWebRequest(src, WebRequestMethods.Ftp.GetFileSize).GetResponse()) { 471. size = webresp.ContentLength; 472. webresp.Dispose(); 473. webresp.Close(); 474. return size; 475. } 476. } 477. catch (System.Exception ex) { 478. if (webresp != null) { 479. webresp.Dispose(); 480. webresp.Close(); 481. } 482. 483. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("获取FTP服务器端文件大小时产生异常:{0}", ex.ToString()), MessageType = LogType.Info }); 484. return 0; 485. } 486. } 487. 488. /// <summary> 489. /// 单个文件上传完毕,触发此事件 490. /// </summary> 491. /// <param name="sender"></param> 492. /// <param name="e"></param> 493. private void FtpHelper_onUploadFileFinishedEvent(object sender, LoadFileEventArgs e){ 494. //String content = GetRemoteFileDetails(e.Src); 495. long size = GetRemoteFileSize(e.Src); 496. if (size == 0) { 497. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("###当前上传的文件可能出现异常或者远程无法连接上,文件源路径:{0}; 上传服务器路径:{1}", e.Des, e.Src), MessageType = LogType.Error }); 498. } 499. else 500. { 501. if (size == e.DesSize){ 502. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("###文件上传成功文件大小为{0}, 服务器路径:{1}", e.DesSize, e.Src), MessageType = LogType.Error }); 503. this.pathQueue.Dequeue();//删除准备出栈的队列 504. 505. try 506. { 507. //删除本地文件 508. System.IO.File.Delete(e.Des); 509. } 510. catch (System.Exception ex) 511. { 512. SaveLog.WriteLog(new LogObj() { MessageText = String.Format("###删除本地文件失败,异常原因:{0}", ex.Message), MessageType = LogType.Error }); 513. } 514. } 515. else { 516. SaveLog.WriteLog(new LogObj() { MessageText = "###文件上传完毕,但本地与远程文件大小不一致,将删除远程文件再重新上传。", MessageType = LogType.Error }); 517. DelRemoteFile(e.Src); 518. } 519. } 520. } 521. 522. private void FtpHelper_onUploadFileEvent(object sender, LoadFileEventArgs e){ 523. 524. } 525. 526. private void FtpHelper_onMonitorUploadEvent(object sender, EventArgs e){ 527. Task.Factory.StartNew(() => { 528. while (true) { 529. if (FTPConnonect()){ 530. if (!IsWorking && this.pathQueue != null && this.pathQueue.Count > 0){ 531. PerUpload(); 532. System.Threading.Thread.Sleep(5000); 533. } 534. else if (!IsWorking && (this.pathQueue == null || this.pathQueue.Count == 0)){ 535. UploadDirectory(); 536. System.Threading.Thread.Sleep(5000); 537. } 538. else if (IsWorking){ 539. System.Threading.Thread.Sleep(5000); 540. } 541. } 542. else 543. System.Threading.Thread.Sleep(10000); 544. } 545. }); 546. } 547. 548. /// <summary> 549. /// 上传文件到FTP服务端 550. /// </summary> 551. /// <param name="src">本地文件路径</param> 552. public void Put(String src) { 553. String path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, src); 554. InsertQueue(path, String.Empty); 555. } 556. } 该文章在 2024/1/12 17:07:11 编辑过 |
关键字查询
相关文章
正在查询... |