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