C# FTP操作
1 using System; 2 using System.Collections.Generic; 3 using System.Net; 4 using System.IO; 5 6 namespace FTP操作 7 { 8 /// <summary> 9 /// FTP客户端操作类 10 /// </summary> 11 public class FtpClient 12 { 13 #region 构造函数 14 /// <summary> 15 /// 创建FTP工具 16 /// <para> 17 /// 默认不使用SSL,使用二进制传输方式,使用被动模式 18 /// </para> 19 /// </summary> 20 /// <param name="host">主机名称</param> 21 /// <param name="userId">用户名</param> 22 /// <param name="password">密码</param> 23 public FtpClient(string host, string userId, string password) 24 : this(host, userId, password, 21, null, false, true, true) 25 { 26 } 27 28 /// <summary> 29 /// 创建FTP工具 30 /// </summary> 31 /// <param name="host">主机名称</param> 32 /// <param name="userId">用户名</param> 33 /// <param name="password">密码</param> 34 /// <param name="port">端口</param> 35 /// <param name="enableSsl">允许Ssl</param> 36 /// <param name="proxy">代理</param> 37 /// <param name="useBinary">允许二进制</param> 38 /// <param name="usePassive">允许被动模式</param> 39 public FtpClient(string host, string userId, string password, int port, IWebProxy proxy, bool enableSsl, bool useBinary, bool usePassive) 40 { 41 this.userId = userId; 42 this.password = password; 43 if (host.ToLower().StartsWith("ftp://")) 44 { 45 this.host = host; 46 } 47 else 48 { 49 this.host = "ftp://" + host; 50 } 51 this.port = port; 52 this.proxy = proxy; 53 this.enableSsl = enableSsl; 54 this.useBinary = useBinary; 55 this.usePassive = usePassive; 56 } 57 #endregion 58 59 #region 主机 60 private string host = string.Empty; 61 /// <summary> 62 /// 主机 63 /// </summary> 64 public string Host 65 { 66 get 67 { 68 return this.host ?? string.Empty; 69 } 70 } 71 #endregion 72 73 #region 登录用户名 74 private string userId = string.Empty; 75 /// <summary> 76 /// 登录用户名 77 /// </summary> 78 public string UserId 79 { 80 get 81 { 82 return this.userId; 83 } 84 } 85 #endregion 86 87 #region 密码 88 private string password = string.Empty; 89 /// <summary> 90 /// 密码 91 /// </summary> 92 public string Password 93 { 94 get 95 { 96 return this.password; 97 } 98 } 99 #endregion 100 101 #region 代理 102 IWebProxy proxy = null; 103 /// <summary> 104 /// 代理 105 /// </summary> 106 public IWebProxy Proxy 107 { 108 get 109 { 110 return this.proxy; 111 } 112 set 113 { 114 this.proxy = value; 115 } 116 } 117 #endregion 118 119 #region 端口 120 private int port = 21; 121 /// <summary> 122 /// 端口 123 /// </summary> 124 public int Port 125 { 126 get 127 { 128 return port; 129 } 130 set 131 { 132 this.port = value; 133 } 134 } 135 #endregion 136 137 #region 设置是否允许Ssl 138 private bool enableSsl = false; 139 /// <summary> 140 /// EnableSsl 141 /// </summary> 142 public bool EnableSsl 143 { 144 get 145 { 146 return enableSsl; 147 } 148 } 149 #endregion 150 151 #region 使用被动模式 152 private bool usePassive = true; 153 /// <summary> 154 /// 被动模式 155 /// </summary> 156 public bool UsePassive 157 { 158 get 159 { 160 return usePassive; 161 } 162 set 163 { 164 this.usePassive = value; 165 } 166 } 167 #endregion 168 169 #region 二进制方式 170 private bool useBinary = true; 171 /// <summary> 172 /// 二进制方式 173 /// </summary> 174 public bool UseBinary 175 { 176 get 177 { 178 return useBinary; 179 } 180 set 181 { 182 this.useBinary = value; 183 } 184 } 185 #endregion 186 187 #region 远端路径 188 private string remotePath = "/"; 189 /// <summary> 190 /// 远端路径 191 /// <para> 192 /// 返回FTP服务器上的当前路径(可以是 / 或 /a/../ 的形式) 193 /// </para> 194 /// </summary> 195 public string RemotePath 196 { 197 get 198 { 199 return remotePath; 200 } 201 set 202 { 203 string result = "/"; 204 if (!string.IsNullOrEmpty(value) && value != "/") 205 { 206 result = "/" + value.TrimStart('/').TrimEnd('/') + "/"; 207 } 208 this.remotePath = result; 209 } 210 } 211 #endregion 212 213 #region 创建一个FTP连接 214 /// <summary> 215 /// 创建一个FTP请求 216 /// </summary> 217 /// <param name="url">请求地址</param> 218 /// <param name="method">请求方法</param> 219 /// <returns>FTP请求</returns> 220 private FtpWebRequest CreateRequest(string url, string method) 221 { 222 //建立连接 223 FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url); 224 request.Credentials = new NetworkCredential(this.userId, this.password); 225 request.Proxy = this.proxy; 226 request.KeepAlive = false;//命令执行完毕之后关闭连接 227 request.UseBinary = useBinary; 228 request.UsePassive = usePassive; 229 request.EnableSsl = enableSsl; 230 request.Method = method; 231 return request; 232 } 233 #endregion 234 235 #region 上传一个文件到远端路径下 236 /// <summary> 237 /// 把文件上传到FTP服务器的RemotePath下 238 /// </summary> 239 /// <param name="localFile">本地文件信息</param> 240 /// <param name="remoteFileName">要保存到FTP文件服务器上的名称</param> 241 public bool Upload(FileInfo localFile, string remoteFileName) 242 { 243 bool result = false; 244 if (localFile.Exists) 245 { 246 string url = Host.TrimEnd('/') + RemotePath + remoteFileName; 247 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.UploadFile); 248 249 //上传数据 250 using (Stream rs = request.GetRequestStream()) 251 using (FileStream fs = localFile.OpenRead()) 252 { 253 byte[] buffer = new byte[4096];//4K 254 int count = fs.Read(buffer, 0, buffer.Length); 255 while (count > 0) 256 { 257 rs.Write(buffer, 0, count); 258 count = fs.Read(buffer, 0, buffer.Length); 259 } 260 fs.Close(); 261 result = true; 262 } 263 return result; 264 } 265 throw new Exception(string.Format("本地文件不存在,文件路径:{0}", localFile.FullName)); 266 } 267 #endregion 268 269 #region 从FTP服务器上下载文件 270 /// <summary> 271 /// 从当前目录下下载文件 272 /// <para> 273 /// 如果本地文件存在,则从本地文件结束的位置开始下载. 274 /// </para> 275 /// </summary> 276 /// <param name="serverName">服务器上的文件名称</param> 277 /// <param name="localName">本地文件名称</param> 278 /// <returns>返回一个值,指示是否下载成功</returns> 279 public bool Download(string serverName, string localName) 280 { 281 bool result = false; 282 using (FileStream fs = new FileStream(localName, FileMode.OpenOrCreate)) //创建或打开本地文件 283 { 284 //建立连接 285 string url = Host.TrimEnd('/') + RemotePath + serverName; 286 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.DownloadFile); 287 request.ContentOffset = fs.Length; 288 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 289 { 290 fs.Position = fs.Length; 291 byte[] buffer = new byte[4096];//4K 292 int count = response.GetResponseStream().Read(buffer, 0, buffer.Length); 293 while (count > 0) 294 { 295 fs.Write(buffer, 0, count); 296 count = response.GetResponseStream().Read(buffer, 0, buffer.Length); 297 } 298 response.GetResponseStream().Close(); 299 } 300 result = true; 301 } 302 return result; 303 } 304 #endregion 305 306 #region 重命名FTP服务器上的文件 307 /// <summary> 308 /// 文件更名 309 /// </summary> 310 /// <param name="oldFileName">原文件名</param> 311 /// <param name="newFileName">新文件名</param> 312 /// <returns>返回一个值,指示更名是否成功</returns> 313 public bool Rename(string oldFileName, string newFileName) 314 { 315 bool result = false; 316 //建立连接 317 string url = Host.TrimEnd('/') + RemotePath + oldFileName; 318 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.Rename); 319 request.RenameTo = newFileName; 320 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 321 { 322 result = true; 323 } 324 return result; 325 } 326 #endregion 327 328 #region 从当前目录下获取文件列表 329 /// <summary> 330 /// 获取当前目录下文件列表 331 /// </summary> 332 /// <returns></returns> 333 public List<string> GetFileList() 334 { 335 List<string> result = new List<string>(); 336 //建立连接 337 string url = Host.TrimEnd('/') + RemotePath; 338 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.ListDirectory); 339 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 340 { 341 StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default);//中文文件名 342 string line = reader.ReadLine(); 343 while (line != null) 344 { 345 result.Add(line); 346 line = reader.ReadLine(); 347 } 348 } 349 return result; 350 } 351 #endregion 352 353 #region 从FTP服务器上获取文件和文件夹列表 354 /// <summary> 355 /// 获取详细列表 356 /// </summary> 357 /// <returns></returns> 358 public List<string> GetFileDetails() 359 { 360 List<string> result = new List<string>(); 361 //建立连接 362 string url = Host.TrimEnd('/') + RemotePath; 363 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.ListDirectoryDetails); 364 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 365 { 366 StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default);//中文文件名 367 string line = reader.ReadLine(); 368 while (line != null) 369 { 370 result.Add(line); 371 line = reader.ReadLine(); 372 } 373 } 374 return result; 375 } 376 #endregion 377 378 #region 从FTP服务器上删除文件 379 /// <summary> 380 /// 删除FTP服务器上的文件 381 /// </summary> 382 /// <param name="fileName">文件名称</param> 383 /// <returns>返回一个值,指示是否删除成功</returns> 384 public bool DeleteFile(string fileName) 385 { 386 bool result = false; 387 //建立连接 388 string url = Host.TrimEnd('/') + RemotePath + fileName; 389 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.DeleteFile); 390 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 391 { 392 result = true; 393 } 394 395 return result; 396 } 397 #endregion 398 399 #region 在FTP服务器上创建目录 400 /// <summary> 401 /// 在当前目录下创建文件夹 402 /// </summary> 403 /// <param name="dirName">文件夹名称</param> 404 /// <returns>返回一个值,指示是否创建成功</returns> 405 public bool MakeDirectory(string dirName) 406 { 407 bool result = false; 408 //建立连接 409 string url = Host.TrimEnd('/') + RemotePath + dirName; 410 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.MakeDirectory); 411 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 412 { 413 result = true; 414 } 415 return result; 416 } 417 #endregion 418 419 #region 从FTP服务器上删除目录 420 /// <summary> 421 /// 删除文件夹 422 /// </summary> 423 /// <param name="dirName">文件夹名称</param> 424 /// <returns>返回一个值,指示是否删除成功</returns> 425 public bool DeleteDirectory(string dirName) 426 { 427 bool result = false; 428 //建立连接 429 string url = Host.TrimEnd('/') + RemotePath + dirName; 430 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.RemoveDirectory); 431 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 432 { 433 result = true; 434 } 435 return result; 436 } 437 #endregion 438 439 #region 从FTP服务器上获取文件大小 440 /// <summary> 441 /// 获取文件大小 442 /// </summary> 443 /// <param name="fileName"></param> 444 /// <returns></returns> 445 public long GetFileSize(string fileName) 446 { 447 long result = 0; 448 //建立连接 449 string url = Host.TrimEnd('/') + RemotePath + fileName; 450 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.GetFileSize); 451 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 452 { 453 result = response.ContentLength; 454 } 455 456 return result; 457 } 458 #endregion 459 460 #region 给FTP服务器上的文件追加内容 461 /// <summary> 462 /// 给FTP服务器上的文件追加内容 463 /// </summary> 464 /// <param name="localFile">本地文件</param> 465 /// <param name="remoteFileName">FTP服务器上的文件</param> 466 /// <returns>返回一个值,指示是否追加成功</returns> 467 public bool Append(FileInfo localFile, string remoteFileName) 468 { 469 if (localFile.Exists) 470 { 471 using (FileStream fs = new FileStream(localFile.FullName, FileMode.Open)) 472 { 473 return Append(fs, remoteFileName); 474 } 475 } 476 throw new Exception(string.Format("本地文件不存在,文件路径:{0}", localFile.FullName)); 477 } 478 479 /// <summary> 480 /// 给FTP服务器上的文件追加内容 481 /// </summary> 482 /// <param name="stream">数据流(可通过设置偏移来实现从特定位置开始上传)</param> 483 /// <param name="remoteFileName">FTP服务器上的文件</param> 484 /// <returns>返回一个值,指示是否追加成功</returns> 485 public bool Append(Stream stream, string remoteFileName) 486 { 487 bool result = false; 488 if (stream != null && stream.CanRead) 489 { 490 //建立连接 491 string url = Host.TrimEnd('/') + RemotePath + remoteFileName; 492 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.AppendFile); 493 using (Stream rs = request.GetRequestStream()) 494 { 495 //上传数据 496 byte[] buffer = new byte[4096];//4K 497 int count = stream.Read(buffer, 0, buffer.Length); 498 while (count > 0) 499 { 500 rs.Write(buffer, 0, count); 501 count = stream.Read(buffer, 0, buffer.Length); 502 } 503 result = true; 504 } 505 } 506 return result; 507 } 508 #endregion 509 510 #region 获取FTP服务器上的当前路径 511 /// <summary> 512 /// 获取FTP服务器上的当前路径 513 /// </summary> 514 public string CurrentDirectory 515 { 516 get 517 { 518 string result = string.Empty; 519 string url = Host.TrimEnd('/') + RemotePath; 520 FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.PrintWorkingDirectory); 521 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) 522 { 523 string temp = response.StatusDescription; 524 int start = temp.IndexOf('"') + 1; 525 int end = temp.LastIndexOf('"'); 526 if (end >= start) 527 { 528 result = temp.Substring(start, end - start); 529 } 530 } 531 return result; 532 533 } 534 } 535 #endregion 536 537 #region 检查当前路径上是否存在某个文件 538 /// <summary> 539 /// 检查文件是否存在 540 /// </summary> 541 /// <param name="fileName">要检查的文件名</param> 542 /// <returns>返回一个值,指示要检查的文件是否存在</returns> 543 public bool CheckFileExist(string fileName) 544 { 545 bool result = false; 546 if (fileName != null && fileName.Trim().Length > 0) 547 { 548 fileName = fileName.Trim(); 549 List<string> files = GetFileList(); 550 if (files != null && files.Count > 0) 551 { 552 foreach (string file in files) 553 { 554 if (file.ToLower() == fileName.ToLower()) 555 { 556 result = true; 557 break; 558 } 559 } 560 } 561 } 562 return result; 563 } 564 #endregion 565 566 } 567 } 568 569 /* 570 FTP全状态码查询词典 571 572 1xx - 肯定的初步答复 573 这些状态代码指示一项操作已经成功开始,但客户端希望在继续操作新命令前得到另一个答复。 • 110 重新启动标记答复。 574 • 120 服务已就绪,在 nnn 分钟后开始。 575 • 125 数据连接已打开,正在开始传输。 576 • 150 文件状态正常,准备打开数据连接。 577 578 2xx - 肯定的完成答复 579 一项操作已经成功完成。客户端可以执行新命令。 • 200 命令确定。 580 • 202 未执行命令,站点上的命令过多。 581 • 211 系统状态,或系统帮助答复。 582 • 212 目录状态。 583 • 213 文件状态。 584 • 214 帮助消息。 585 • 215 NAME 系统类型,其中,NAME 是 Assigned Numbers 文档中所列的正式系统名称。 586 • 220 服务就绪,可以执行新用户的请求。 587 • 221 服务关闭控制连接。如果适当,请注销。 588 • 225 数据连接打开,没有进行中的传输。 589 • 226 关闭数据连接。请求的文件操作已成功(例如,传输文件或放弃文件)。 590 • 227 进入被动模式 (h1,h2,h3,h4,p1,p2)。 591 • 230 用户已登录,继续进行。 592 • 250 请求的文件操作正确,已完成。 593 • 257 已创建“PATHNAME”。 594 595 3xx - 肯定的中间答复 596 该命令已成功,但服务器需要更多来自客户端的信息以完成对请求的处理。 • 331 用户名正确,需要密码。 597 • 332 需要登录帐户。 598 • 350 请求的文件操作正在等待进一步的信息。 599 600 4xx - 瞬态否定的完成答复 601 该命令不成功,但错误是暂时的。如果客户端重试命令,可能会执行成功。 • 421 服务不可用,正在关闭控制连接。如果服务确定它必须关闭,将向任何命令发送这一应答。 602 • 425 无法打开数据连接。 603 • 426 Connection closed; transfer aborted. 604 • 450 未执行请求的文件操作。文件不可用(例如,文件繁忙)。 605 • 451 请求的操作异常终止:正在处理本地错误。 606 • 452 未执行请求的操作。系统存储空间不够。 607 608 5xx - 永久性否定的完成答复 609 该命令不成功,错误是永久性的。如果客户端重试命令,将再次出现同样的错误。 • 500 语法错误,命令无法识别。这可能包括诸如命令行太长之类的错误。 610 • 501 在参数中有语法错误。 611 • 502 未执行命令。 612 • 503 错误的命令序列。 613 • 504 未执行该参数的命令。 614 • 530 未登录。 615 • 532 存储文件需要帐户。 616 • 550 未执行请求的操作。文件不可用(例如,未找到文件,没有访问权限)。 617 • 551 请求的操作异常终止:未知的页面类型。 618 • 552 请求的文件操作异常终止:超出存储分配(对于当前目录或数据集)。 619 • 553 未执行请求的操作。不允许的文件名。 620 常见的 FTP 状态代码及其原因 621 • 150 - FTP 使用两个端口:21 用于发送命令,20 用于发送数据。状态代码 150 表示服务器准备在端口 20 上打开新连接,发送一些数据。 622 • 226 - 命令在端口 20 上打开数据连接以执行操作,如传输文件。该操作成功完成,数据连接已关闭。 623 • 230 - 客户端发送正确的密码后,显示该状态代码。它表示用户已成功登录。 624 • 331 - 客户端发送用户名后,显示该状态代码。无论所提供的用户名是否为系统中的有效帐户,都将显示该状态代码。 625 • 426 - 命令打开数据连接以执行操作,但该操作已被取消,数据连接已关闭。 626 • 530 - 该状态代码表示用户无法登录,因为用户名和密码组合无效。如果使用某个用户帐户登录,可能键入错误的用户名或密码,也可能选择只允许匿名访问。如果使用匿名帐户登录,IIS 的配置可能拒绝匿名访问。 627 • 550 - 命令未被执行,因为指定的文件不可用。例如,要 GET 的文件并不存在,或试图将文件 PUT 到您没有写入权限的目录。 628 */