FTP客户端类,C#
最近在搞FTP,挺头大的,后来索性在网上找了个FTP的类,直接调用函数即可。由于网上的版本较多,我用的这个测试了下,比较好用。现共享给大家。
1 using System;
2 using System.Net;
3 using System.IO;
4 using System.Text;
5 using System.Net.Sockets;
6
7 namespace WindowsFormsApplication1
8 {
9 public class FTPClient
10 {
11 #region 构造函数
12 /// <summary>
13 /// 缺省构造函数
14 /// </summary>
15 public FTPClient()
16 {
17 strRemoteHost = "";
18 strRemotePath = "";
19 strRemoteUser = "";
20 strRemotePass = "";
21 strRemotePort = 21;
22 bConnected = false;
23 }
24
25 /// <summary>
26 /// 构造函数
27 /// </summary>
28 /// <param name="remoteHost"></param>
29 /// <param name="remotePath"></param>
30 /// <param name="remoteUser"></param>
31 /// <param name="remotePass"></param>
32 /// <param name="remotePort"></param>
33 public FTPClient(string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort)
34 {
35 strRemoteHost = remoteHost;
36 strRemotePath = remotePath;
37 strRemoteUser = remoteUser;
38 strRemotePass = remotePass;
39 strRemotePort = remotePort;
40 Connect();
41 }
42 #endregion
43
44 #region 登陆
45 /// <summary>
46 /// FTP服务器IP地址
47 /// </summary>
48 private string strRemoteHost;
49 public string RemoteHost
50 {
51 get
52 {
53 return strRemoteHost;
54 }
55 set
56 {
57 strRemoteHost = value;
58 }
59 }
60 /// <summary>
61 /// FTP服务器端口
62 /// </summary>
63 private int strRemotePort;
64 public int RemotePort
65 {
66 get
67 {
68 return strRemotePort;
69 }
70 set
71 {
72 strRemotePort = value;
73 }
74 }
75 /// <summary>
76 /// 当前服务器目录
77 /// </summary>
78 private string strRemotePath;
79 public string RemotePath
80 {
81 get
82 {
83 return strRemotePath;
84 }
85 set
86 {
87 strRemotePath = value;
88 }
89 }
90 /// <summary>
91 /// 登录用户账号
92 /// </summary>
93 private string strRemoteUser;
94 public string RemoteUser
95 {
96 set
97 {
98 strRemoteUser = value;
99 }
100 }
101 /// <summary>
102 /// 用户登录密码
103 /// </summary>
104 private string strRemotePass;
105 public string RemotePass
106 {
107 set
108 {
109 strRemotePass = value;
110 }
111 }
112
113 /// <summary>
114 /// 是否登录
115 /// </summary>
116 private Boolean bConnected;
117 public bool Connected
118 {
119 get
120 {
121 return bConnected;
122 }
123 }
124 #endregion
125
126 #region 链接
127 /// <summary>
128 /// 建立连接
129 /// </summary>
130 public void Connect()
131 {
132 socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
133 IPEndPoint ep = new IPEndPoint(IPAddress.Parse(RemoteHost), strRemotePort);
134 // 链接
135 try
136 {
137 socketControl.Connect(ep);
138 }
139 catch (Exception)
140 {
141 throw new IOException("Couldn't connect to remote server");
142 }
143
144 // 获取应答码
145 ReadReply();
146 if (iReplyCode != 220)
147 {
148 DisConnect();
149 throw new IOException(strReply.Substring(4));
150 }
151
152 // 登陆
153 SendCommand("USER " + strRemoteUser);
154 if (!(iReplyCode == 331 || iReplyCode == 230))
155 {
156 CloseSocketConnect();//关闭连接
157 throw new IOException(strReply.Substring(4));
158 }
159 if (iReplyCode != 230)
160 {
161 SendCommand("PASS " + strRemotePass);
162 if (!(iReplyCode == 230 || iReplyCode == 202))
163 {
164 CloseSocketConnect();//关闭连接
165 throw new IOException(strReply.Substring(4));
166 }
167 }
168 bConnected = true;
169
170 // 切换到目录
171 ChDir(strRemotePath);
172 }
173
174
175 /// <summary>
176 /// 关闭连接
177 /// </summary>
178 public void DisConnect()
179 {
180 if (socketControl != null)
181 {
182 SendCommand("QUIT");
183 }
184 CloseSocketConnect();
185 }
186
187 #endregion
188
189 #region 传输模式
190
191 /// <summary>
192 /// 传输模式:二进制类型、ASCII类型
193 /// </summary>
194 public enum TransferType { Binary, ASCII };
195
196 /// <summary>
197 /// 设置传输模式
198 /// </summary>
199 /// <param name="ttType">传输模式</param>
200 public void SetTransferType(TransferType ttType)
201 {
202 if (ttType == TransferType.Binary)
203 {
204 SendCommand("TYPE I");//binary类型传输
205 }
206 else
207 {
208 SendCommand("TYPE A");//ASCII类型传输
209 }
210 if (iReplyCode != 200)
211 {
212 throw new IOException(strReply.Substring(4));
213 }
214 else
215 {
216 trType = ttType;
217 }
218 }
219
220
221 /// <summary>
222 /// 获得传输模式
223 /// </summary>
224 /// <returns>传输模式</returns>
225 public TransferType GetTransferType()
226 {
227 return trType;
228 }
229
230 #endregion
231
232 #region 文件操作
233 /// <summary>
234 /// 获得文件列表
235 /// </summary>
236 /// <param name="strMask">文件名的匹配字符串</param>
237 /// <returns></returns>
238 public string[] Dir(string strMask)
239 {
240 // 建立链接
241 if (!bConnected)
242 {
243 Connect();
244 }
245
246 //建立进行数据连接的socket
247 Socket socketData = CreateDataSocket();
248
249 //传送命令
250 SendCommand("NLST " + strMask);
251
252 //分析应答代码
253 if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226))
254 {
255 throw new IOException(strReply.Substring(4));
256 }
257
258 //获得结果
259 strMsg = "";
260 while (true)
261 {
262 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
263 strMsg += Encoding.Default.GetString(buffer, 0, iBytes);
264 if (iBytes < buffer.Length)
265 {
266 break;
267 }
268 }
269 char[] seperator = "\r\n".ToCharArray();
270 string[] strsFileList = strMsg.Split(seperator, StringSplitOptions.RemoveEmptyEntries);
271 socketData.Close();//数据socket关闭时也会有返回码
272 if (iReplyCode != 226)
273 {
274 ReadReply();
275 if (iReplyCode != 226)
276 {
277 throw new IOException(strReply.Substring(4));
278 }
279 }
280 return strsFileList;
281 }
282
283
284 /// <summary>
285 /// 获取文件大小
286 /// </summary>
287 /// <param name="strFileName">文件名</param>
288 /// <returns>文件大小</returns>
289 private long GetFileSize(string strFileName)
290 {
291 if (!bConnected)
292 {
293 Connect();
294 }
295 SendCommand("SIZE " + Path.GetFileName(strFileName));
296 long lSize = 0;
297 if (iReplyCode == 213)
298 {
299 lSize = Int64.Parse(strReply.Substring(4));
300 }
301 else
302 {
303 throw new IOException(strReply.Substring(4));
304 }
305 return lSize;
306 }
307
308 /// <summary>
309 /// 获得文件详细信息列表
310 /// </summary>
311 /// <param name="strMask">文件名的匹配字符串</param>
312 /// <returns></returns>
313 public string[] List(string strMask)
314 {
315 // 建立链接
316 if (!bConnected)
317 {
318 Connect();
319 }
320
321 //建立进行数据连接的socket
322 Socket socketData = CreateDataSocket();
323
324 //传送命令
325 SendCommand("LIST " + strMask);
326
327 //分析应答代码
328 if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226))
329 {
330 throw new IOException(strReply.Substring(4));
331 }
332
333 //获得结果
334 strMsg = "";
335 while (true)
336 {
337 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
338 strMsg += Encoding.Default.GetString(buffer, 0, iBytes);
339 if (iBytes < buffer.Length)
340 {
341 break;
342 }
343 }
344 char[] seperator = "\r\n".ToCharArray();
345 string[] strsFileList = strMsg.Split(seperator, StringSplitOptions.RemoveEmptyEntries);
346 socketData.Close();//数据socket关闭时也会有返回码
347 if (iReplyCode != 226)
348 {
349 ReadReply();
350 if (iReplyCode != 226)
351 {
352 throw new IOException(strReply.Substring(4));
353 }
354 }
355 return strsFileList;
356 }
357
358
359 /// <summary>
360 /// 删除
361 /// </summary>
362 /// <param name="strFileName">待删除文件名</param>
363 public void Delete(string strFileName)
364 {
365 if (!bConnected)
366 {
367 Connect();
368 }
369 SendCommand("DELE " + strFileName);
370 if (iReplyCode != 250)
371 {
372 throw new IOException(strReply.Substring(4));
373 }
374 }
375
376
377 /// <summary>
378 /// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)
379 /// </summary>
380 /// <param name="strOldFileName">旧文件名</param>
381 /// <param name="strNewFileName">新文件名</param>
382 public void Rename(string strOldFileName, string strNewFileName)
383 {
384 if (!bConnected)
385 {
386 Connect();
387 }
388 SendCommand("RNFR " + strOldFileName);
389 if (iReplyCode != 350)
390 {
391 throw new IOException(strReply.Substring(4));
392 }
393 // 如果新文件名与原有文件重名,将覆盖原有文件
394 SendCommand("RNTO " + strNewFileName);
395 if (iReplyCode != 250)
396 {
397 throw new IOException(strReply.Substring(4));
398 }
399 }
400 #endregion
401
402 #region 上传和下载
403 /// <summary>
404 /// 下载一批文件
405 /// </summary>
406 /// <param name="strFileNameMask">文件名的匹配字符串</param>
407 /// <param name="strFolder">本地目录(不得以\结束)</param>
408 public void Get(string strFileNameMask, string strFolder)
409 {
410 if (!bConnected)
411 {
412 Connect();
413 }
414 string[] strFiles = Dir(strFileNameMask);
415 foreach (string strFile in strFiles)
416 {
417 if (!strFile.Equals(""))//一般来说strFiles的最后一个元素可能是空字符串
418 {
419 Get(strFile, strFolder, strFile);
420 }
421 }
422 }
423
424
425 /// <summary>
426 /// 下载一个文件
427 /// </summary>
428 /// <param name="strRemoteFileName">要下载的文件名</param>
429 /// <param name="strFolder">本地目录(不得以\结束)</param>
430 /// <param name="strLocalFileName">保存在本地时的文件名</param>
431 public void Get(string strRemoteFileName, string strFolder, string strLocalFileName)
432 {
433 if (!bConnected)
434 {
435 Connect();
436 }
437 SetTransferType(TransferType.Binary);
438 if (strLocalFileName.Equals(""))
439 {
440 strLocalFileName = strRemoteFileName;
441 }
442 if (!File.Exists(strLocalFileName))
443 {
444 Stream st = File.Create(strLocalFileName);
445 st.Close();
446 }
447 FileStream output = new
448 FileStream(strFolder + "\\" + strLocalFileName, FileMode.Create);
449 Socket socketData = CreateDataSocket();
450 SendCommand("RETR " + strRemoteFileName);
451 if (!(iReplyCode == 150 || iReplyCode == 125
452 || iReplyCode == 226 || iReplyCode == 250))
453 {
454 throw new IOException(strReply.Substring(4));
455 }
456 while (true)
457 {
458 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
459 output.Write(buffer, 0, iBytes);
460 if (iBytes <= 0)
461 {
462 break;
463 }
464 }
465 output.Close();
466 if (socketData.Connected)
467 {
468 socketData.Close();
469 }
470 if (!(iReplyCode == 226 || iReplyCode == 250))
471 {
472 ReadReply();
473 if (!(iReplyCode == 226 || iReplyCode == 250))
474 {
475 throw new IOException(strReply.Substring(4));
476 }
477 }
478 }
479
480
481 /// <summary>
482 /// 上传一批文件
483 /// </summary>
484 /// <param name="strFolder">本地目录(不得以\结束)</param>
485 /// <param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param>
486 public void Put(string strFolder, string strFileNameMask)
487 {
488 string[] strFiles = Directory.GetFiles(strFolder, strFileNameMask);
489 foreach (string strFile in strFiles)
490 {
491 //strFile是完整的文件名(包含路径)
492 Put(strFile);
493 }
494 }
495
496
497 /// <summary>
498 /// 上传一个文件
499 /// </summary>
500 /// <param name="strFileName">本地文件名</param>
501 public void Put(string strFileName)
502 {
503 if (!bConnected)
504 {
505 Connect();
506 }
507 Socket socketData = CreateDataSocket();
508 SendCommand("STOR " + Path.GetFileName(strFileName));
509 if (!(iReplyCode == 125 || iReplyCode == 150))
510 {
511 throw new IOException(strReply.Substring(4));
512 }
513 FileStream input = new
514 FileStream(strFileName, FileMode.Open);
515 int iBytes = 0;
516 while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0)
517 {
518 socketData.Send(buffer, iBytes, 0);
519 }
520 input.Close();
521 if (socketData.Connected)
522 {
523 socketData.Close();
524 }
525 if (!(iReplyCode == 226 || iReplyCode == 250))
526 {
527 ReadReply();
528 if (!(iReplyCode == 226 || iReplyCode == 250))
529 {
530 throw new IOException(strReply.Substring(4));
531 }
532 }
533 }
534
535 #endregion
536
537 #region 目录操作
538 /// <summary>
539 /// 创建目录
540 /// </summary>
541 /// <param name="strDirName">目录名</param>
542 public void MkDir(string strDirName)
543 {
544 if (!bConnected)
545 {
546 Connect();
547 }
548 SendCommand("MKD " + strDirName);
549 if (iReplyCode != 257)
550 {
551 throw new IOException(strReply.Substring(4));
552 }
553 }
554
555
556 /// <summary>
557 /// 删除目录
558 /// </summary>
559 /// <param name="strDirName">目录名</param>
560 public void RmDir(string strDirName)
561 {
562 if (!bConnected)
563 {
564 Connect();
565 }
566 SendCommand("RMD " + strDirName);
567 if (iReplyCode != 250)
568 {
569 throw new IOException(strReply.Substring(4));
570 }
571 }
572
573
574 /// <summary>
575 /// 改变目录
576 /// </summary>
577 /// <param name="strDirName">新的工作目录名</param>
578 public void ChDir(string strDirName)
579 {
580 if (strDirName.Equals(".") || strDirName.Equals(""))
581 {
582 return;
583 }
584 if (!bConnected)
585 {
586 Connect();
587 }
588 SendCommand("CWD " + strDirName);
589 if (iReplyCode != 250)
590 {
591 throw new IOException(strReply.Substring(4));
592 }
593 strRemotePath = strDirName;
594 }
595
596 #endregion
597
598 #region 内部变量
599 /// <summary>
600 /// 服务器返回的应答信息(包含应答码)
601 /// </summary>
602 private string strMsg;
603 /// <summary>
604 /// 服务器返回的应答信息(包含应答码)
605 /// </summary>
606 private string strReply;
607 /// <summary>
608 /// 服务器返回的应答码
609 /// </summary>
610 private int iReplyCode;
611 /// <summary>
612 /// 进行控制连接的socket
613 /// </summary>
614 private Socket socketControl;
615 /// <summary>
616 /// 传输模式
617 /// </summary>
618 private TransferType trType;
619 /// <summary>
620 /// 接收和发送数据的缓冲区
621 /// </summary>
622 private static int BLOCK_SIZE = 512;
623 Byte[] buffer = new Byte[BLOCK_SIZE];
624 /// <summary>
625 /// 编码方式
626 /// </summary>
627 Encoding ASCII = Encoding.ASCII;
628 #endregion
629
630 #region 内部函数
631 /// <summary>
632 /// 将一行应答字符串记录在strReply和strMsg
633 /// 应答码记录在iReplyCode
634 /// </summary>
635 private void ReadReply()
636 {
637 strMsg = "";
638 strReply = ReadLine();
639 iReplyCode = Int32.Parse(strReply.Substring(0, 3));
640 }
641
642 /// <summary>
643 /// 建立进行数据连接的socket
644 /// </summary>
645 /// <returns>数据连接socket</returns>
646 private Socket CreateDataSocket()
647 {
648 SendCommand("PASV");
649 if (iReplyCode != 227)
650 {
651 throw new IOException(strReply.Substring(4));
652 }
653 int index1 = strReply.IndexOf('(');
654 int index2 = strReply.IndexOf(')');
655 string ipData =
656 strReply.Substring(index1 + 1, index2 - index1 - 1);
657 int[] parts = new int[6];
658 int len = ipData.Length;
659 int partCount = 0;
660 string buf = "";
661 for (int i = 0; i < len && partCount <= 6; i++)
662 {
663 char ch = Char.Parse(ipData.Substring(i, 1));
664 if (Char.IsDigit(ch))
665 buf += ch;
666 else if (ch != ',')
667 {
668 throw new IOException("Malformed PASV strReply: " +
669 strReply);
670 }
671 if (ch == ',' || i + 1 == len)
672 {
673 try
674 {
675 parts[partCount++] = Int32.Parse(buf);
676 buf = "";
677 }
678 catch (Exception)
679 {
680 throw new IOException("Malformed PASV strReply: " +
681 strReply);
682 }
683 }
684 }
685 string ipAddress = parts[0] + "." + parts[1] + "." +
686 parts[2] + "." + parts[3];
687 int port = (parts[4] << 8) + parts[5];
688 Socket s = new
689 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
690 IPEndPoint ep = new
691 IPEndPoint(IPAddress.Parse(ipAddress), port);
692 try
693 {
694 s.Connect(ep);
695 }
696 catch (Exception)
697 {
698 throw new IOException("Can't connect to remote server");
699 }
700 return s;
701 }
702
703
704 /// <summary>
705 /// 关闭socket连接(用于登录以前)
706 /// </summary>
707 private void CloseSocketConnect()
708 {
709 if (socketControl != null)
710 {
711 socketControl.Close();
712 socketControl = null;
713 }
714 bConnected = false;
715 }
716
717 /// <summary>
718 /// 读取Socket返回的所有字符串
719 /// </summary>
720 /// <returns>包含应答码的字符串行</returns>
721 private string ReadLine()
722 {
723 while (true)
724 {
725 int iBytes = socketControl.Receive(buffer, buffer.Length, 0);
726 strMsg += ASCII.GetString(buffer, 0, iBytes);
727 if (iBytes < buffer.Length)
728 {
729 break;
730 }
731 }
732 char[] seperator = { '\n' };
733 string[] mess = strMsg.Split(seperator);
734 if (strMsg.Length > 2)
735 {
736 strMsg = mess[mess.Length - 2];
737 //seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,
738 //但也会分配为空字符串给后面(也是最后一个)字符串数组,
739 //所以最后一个mess是没用的空字符串
740 //但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
741 }
742 else
743 {
744 strMsg = mess[0];
745 }
746 if (!strMsg.Substring(3, 1).Equals(" "))//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
747 {
748 return ReadLine();
749 }
750 return strMsg;
751 }
752
753
754 /// <summary>
755 /// 发送命令并获取应答码和最后一行应答字符串
756 /// </summary>
757 /// <param name="strCommand">命令</param>
758 private void SendCommand(String strCommand)
759 {
760 Byte[] cmdBytes =
761 Encoding.Default.GetBytes((strCommand + "\r\n").ToCharArray());
762 socketControl.Send(cmdBytes, cmdBytes.Length, 0);
763 ReadReply();
764 }
765
766 #endregion
767 }
768 }
769
- 上一篇 »Python ftplib
- 下一篇 »Java连接服务器的两种方式SFTP和FTP