• FTP操作类的使用


    FTP(文件传输协议)

    FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。"下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。

    运行机制

    FTP服务器
    简单地说,支持FTP协议的服务器就是FTP服务器。
    与大多数Internet服务一样,FTP也是一个客户机/服务器系统。用户通过一个支持FTP协议的客户机程序,连接到在远程主机上的FTP服务器程序。用户通过客户机程序向服务器程序发出命令,服务器程序执行用户所发出的命令,并将执行的结果返回到客户机。比如说,用户发出一条命令,要求服务器向用户传送某一个文件的一份拷贝,服务器会响应这条命令,将指定文件送至用户的机器上。客户机程序代表用户接收到这个文件,将其存放在用户目录中。
     

    匿名FTP

    使用FTP时必须首先登录,在远程主机上获得相应的权限以后,方可下载或上传文件。也就是说,要想同哪一台计算机传送文件,就必须具有哪一台计算机的适当授权。换言之,除非有用户ID和口令,否则便无法传送文件。这种情况违背了Internet的开放性,Internet上的FTP主机何止千万,不可能要求每个用户在每一台主机上都拥有帐号。匿名FTP就是为解决这个问题而产生的。
    匿名FTP是这样一种机制,用户可通过它连接到远程主机上,并从其下载文件,而无需成为其注册用户。系统管理员建立了一个特殊的用户ID,名为anonymous, Internet上的任何人在任何地方都可使用该用户ID。
    通过FTP程序连接匿名FTP主机的方式同连接普通FTP主机的方式差不多,只是在要求提供用户标识ID时必须输入anonymous,该用户ID的口令可以是任意的字符串。习惯上,用自己的E-mail地址作为口令,使系统维护程序能够记录下来谁在存取这些文件。
    值得注意的是,匿名FTP不适用于所有Internet主机,它只适用于那些提供了这项服务的主机。
    当远程主机提供匿名FTP服务时,会指定某些目录向公众开放,允许匿名存取。系统中的其余目录则处于隐匿状态。作为一种安全措施,大多数匿名FTP主机都允许用户从其下载文件,而不允许用户向其上传文件,也就是说,用户可将匿名FTP主机上的所有文件全部拷贝到自己的机器上,但不能将自己机器上的任何一个文件拷贝至匿名FTP主机上。即使有些匿名FTP主机确实允许用户上传文件,用户也只能将文件上传至某一指定上传目录中。随后,系统管理员会去检查这些文件,他会将这些文件移至另一个公共下载目录中,供其他用户下载,利用这种方式,远程主机的用户得到了保护,避免了有人上传有问题的文件,如带病毒的文件。

    传输方式

    FTP的传输有两种方式:ASCII、二进制。
    ASCII传输方式
    假定用户正在拷贝的文件包含的简单ASCII码文本,如果在远程机器上运行的不是UNIX,当文件传输时ftp通常会自动地调整文件的内容以便于把文件解释成另外那台计算机存储文本文件的格式。
    但是常常有这样的情况,用户正在传输的文件包含的不是文本文件,它们可能是程序,数据库,字处理文件或者压缩文件。在拷贝任何非文本文件之前,用binary 命令告诉ftp逐字拷贝。
    二进制传输模式
    在二进制传输中,保存文件的位序,以便原始和拷贝的是逐位一一对应的。即使目的地机器上包含位序列的文件是没意义的。例如,macintosh以二进制方式传送可执行文件到Windows系统,在对方系统上,此文件不能执行。
    如在ASCII方式下传输二进制文件,即使不需要也仍会转译。这会损坏数据。(ASCII方式一般假设每一字符的第一有效位无意义,因为ASCII字符组合不使用它。如果传输二进制文件,所有的位都是重要的。)
     
    FTPHelper操作类方便使用
    using System; 
    using System.Collections.Generic; 
    using System.Text; 
    using System.IO; 
    using System.Net;
    using System.Text.RegularExpressions;
    
    namespace DotNet.Dal
    {
    public class FTPHelper
    {
    #region 字段
    string ftpURI;
    string ftpUserID;
    string ftpServerIP;
    string ftpPassword;
    string ftpRemotePath;
    #endregion
    
    /// <summary> 
    /// 连接FTP服务器
    /// </summary> 
    /// <param name="FtpServerIP">FTP连接地址</param> 
    /// <param name="FtpRemotePath">指定FTP连接成功后的当前目录, 如果不指定即默认为根目录</param> 
    /// <param name="FtpUserID">用户名</param> 
    /// <param name="FtpPassword">密码</param> 
    public FTPHelper(string FtpServerIP, string FtpRemotePath, string FtpUserID, string FtpPassword)
    {
    ftpServerIP = FtpServerIP;
    ftpRemotePath = FtpRemotePath;
    ftpUserID = FtpUserID;
    ftpPassword = FtpPassword;
    ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
    }
    
    /// <summary> 
    /// 上传 
    /// </summary> 
    public void Upload(string filename)
    {
    FileInfo fileInf = new FileInfo(filename);
    FtpWebRequest reqFTP;
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileInf.Name));
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
    reqFTP.KeepAlive = false;
    reqFTP.UseBinary = true;
    reqFTP.ContentLength = fileInf.Length;
    int buffLength = 2048;
    byte[] buff = new byte[buffLength];
    int contentLen;
    FileStream fs = fileInf.OpenRead();
    try
    {
    Stream strm = reqFTP.GetRequestStream();
    contentLen = fs.Read(buff, 0, buffLength);
    while (contentLen != 0)
    {
    strm.Write(buff, 0, contentLen);
    contentLen = fs.Read(buff, 0, buffLength);
    }
    strm.Close();
    fs.Close();
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    }
    
    /// <summary> 
    /// 下载 
    /// </summary> 
    public void Download(string filePath, string fileName)
    {
    try
    {
    FileStream outputStream = new FileStream(filePath + "\" + fileName, FileMode.Create);
    FtpWebRequest reqFTP;
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileName));
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
    reqFTP.UseBinary = true;
    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
    Stream ftpStream = response.GetResponseStream();
    long cl = response.ContentLength;
    int bufferSize = 2048;
    int readCount;
    byte[] buffer = new byte[bufferSize];
    readCount = ftpStream.Read(buffer, 0, bufferSize);
    while (readCount > 0)
    {
    outputStream.Write(buffer, 0, readCount);
    readCount = ftpStream.Read(buffer, 0, bufferSize);
    }
    ftpStream.Close();
    outputStream.Close();
    response.Close();
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    }
    
    /// <summary> 
    /// 删除文件 
    /// </summary> 
    public void Delete(string fileName)
    {
    try
    {
    FtpWebRequest reqFTP;
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileName));
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;
    reqFTP.KeepAlive = false;
    string result = String.Empty;
    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
    long size = response.ContentLength;
    Stream datastream = response.GetResponseStream();
    StreamReader sr = new StreamReader(datastream);
    result = sr.ReadToEnd();
    sr.Close();
    datastream.Close();
    response.Close();
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    }
    
    /// <summary> 
    /// 获取当前目录下明细(包含文件和文件夹) 
    /// </summary> 
    public string[] GetFilesDetailList()
    {
    try
    {
    StringBuilder result = new StringBuilder();
    FtpWebRequest ftp;
    ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
    ftp.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    WebResponse response = ftp.GetResponse();
    StreamReader reader = new StreamReader(response.GetResponseStream());
    string line = reader.ReadLine();
    line = reader.ReadLine();
    line = reader.ReadLine();
    while (line != null)
    {
    result.Append(line);
    result.Append("
    ");
    line = reader.ReadLine();
    }
    result.Remove(result.ToString().LastIndexOf("
    "), 1);
    reader.Close();
    response.Close();
    return result.ToString().Split('
    ');
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    }
    
    /// <summary> 
    /// 获取FTP文件列表(包括文件夹)
    /// </summary> 
    private string[] GetAllList(string url)
    {
    List<string> list = new List<string>();
    FtpWebRequest req = (FtpWebRequest)WebRequest.Create(new Uri(url));
    req.Credentials = new NetworkCredential(ftpPassword, ftpPassword);
    req.Method = WebRequestMethods.Ftp.ListDirectory;
    req.UseBinary = true;
    req.UsePassive = true;
    try
    {
    using (FtpWebResponse res = (FtpWebResponse)req.GetResponse())
    {
    using (StreamReader sr = new StreamReader(res.GetResponseStream()))
    {
    string s;
    while ((s = sr.ReadLine()) != null)
    {
    list.Add(s);
    }
    }
    }
    }
    catch (Exception ex)
    {
    throw (ex);
    }
    return list.ToArray();
    }
    
    /// <summary> 
    /// 获取当前目录下文件列表(不包括文件夹) 
    /// </summary> 
    public string[] GetFileList(string url)
    {
    StringBuilder result = new StringBuilder();
    FtpWebRequest reqFTP;
    try
    {
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
    reqFTP.UseBinary = true;
    reqFTP.Credentials = new NetworkCredential(ftpPassword, ftpPassword);
    reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    WebResponse response = reqFTP.GetResponse();
    StreamReader reader = new StreamReader(response.GetResponseStream());
    string line = reader.ReadLine();
    while (line != null)
    {
    
    if (line.IndexOf("<DIR>") == -1)
    {
    result.Append(Regex.Match(line, @"[S]+ [S]+", RegexOptions.IgnoreCase).Value.Split(' ')[1]);
    result.Append("
    ");
    }
    line = reader.ReadLine();
    }
    result.Remove(result.ToString().LastIndexOf('
    '), 1);
    reader.Close();
    response.Close();
    }
    catch (Exception ex)
    {
    throw (ex);
    }
    return result.ToString().Split('
    ');
    }
    
    /// <summary> 
    /// 判断当前目录下指定的文件是否存在 
    /// </summary> 
    /// <param name="RemoteFileName">远程文件名</param> 
    public bool FileExist(string RemoteFileName)
    {
    string[] fileList = GetFileList("*.*");
    foreach (string str in fileList)
    {
    if (str.Trim() == RemoteFileName.Trim())
    {
    return true;
    }
    }
    return false;
    }
    
    /// <summary> 
    /// 创建文件夹 
    /// </summary> 
    public void MakeDir(string dirName)
    {
    FtpWebRequest reqFTP;
    try
    {
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName));
    reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
    reqFTP.UseBinary = true;
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
    Stream ftpStream = response.GetResponseStream();
    ftpStream.Close();
    response.Close();
    }
    catch (Exception ex)
    { }
    }
    
    /// <summary> 
    /// 获取指定文件大小 
    /// </summary> 
    public long GetFileSize(string filename)
    {
    FtpWebRequest reqFTP;
    long fileSize = 0;
    try
    {
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + filename));
    reqFTP.Method = WebRequestMethods.Ftp.GetFileSize;
    reqFTP.UseBinary = true;
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
    Stream ftpStream = response.GetResponseStream();
    fileSize = response.ContentLength;
    ftpStream.Close();
    response.Close();
    }
    catch (Exception ex)
    { }
    return fileSize;
    }
    
    /// <summary> 
    /// 更改文件名 
    /// </summary> 
    public void ReName(string currentFilename, string newFilename)
    {
    FtpWebRequest reqFTP;
    try
    {
    reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + currentFilename));
    reqFTP.Method = WebRequestMethods.Ftp.Rename;
    reqFTP.RenameTo = newFilename;
    reqFTP.UseBinary = true;
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
    Stream ftpStream = response.GetResponseStream();
    ftpStream.Close();
    response.Close();
    }
    catch (Exception ex)
    { }
    }
    
    /// <summary> 
    /// 移动文件 
    /// </summary> 
    public void MovieFile(string currentFilename, string newDirectory)
    {
    ReName(currentFilename, newDirectory);
    }
    
    /// <summary> 
    /// 切换当前目录 
    /// </summary> 
    /// <param name="IsRoot">true:绝对路径 false:相对路径</param> 
    public void GotoDirectory(string DirectoryName, bool IsRoot)
    {
    if (IsRoot)
    {
    ftpRemotePath = DirectoryName;
    }
    else
    {
    ftpRemotePath += DirectoryName + "/";
    }
    ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
    }
    }
    }
    View Code
    using System;
    using System.Text;
    using System.IO;
    
    namespace DotNet.Dal
    {
    public class FTPOperater
    {
    #region 属性
    private FTPClient ftp;
    /// <summary>
    /// 全局FTP访问变量
    /// </summary>
    public FTPClient Ftp
    {
    get { return ftp; }
    set { ftp = value; }
    }
    
    private string _server;
    /// <summary>
    /// Ftp服务器
    /// </summary>
    public string Server
    {
    get { return _server; }
    set { _server = value; }
    }
    
    private string _User;
    /// <summary>
    /// Ftp用户
    /// </summary>
    public string User
    {
    get { return _User; }
    set { _User = value; }
    }
    
    private string _Pass;
    /// <summary>
    /// Ftp密码
    /// </summary>
    public string Pass
    {
    get { return _Pass; }
    set { _Pass = value; }
    }
    
    private string _FolderZJ;
    /// <summary>
    /// Ftp密码
    /// </summary>
    public string FolderZJ
    {
    get { return _FolderZJ; }
    set { _FolderZJ = value; }
    }
    
    private string _FolderWX;
    /// <summary>
    /// Ftp密码
    /// </summary>
    public string FolderWX
    {
    get { return _FolderWX; }
    set { _FolderWX = value; }
    }
    #endregion
    
    /// <summary>
    /// 得到文件列表
    /// </summary>
    /// <returns></returns>
    public string[] GetList(string strPath)
    {
    if (ftp == null) ftp = this.getFtpClient();
    ftp.Connect();
    ftp.ChDir(strPath);
    return ftp.Dir("*");
    }
    
    /// <summary>
    /// 下载文件
    /// </summary>
    /// <param name="ftpFolder">ftp目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    /// <param name="localFolder">本地目录</param>
    /// <param name="localFileName">本地文件名</param>
    public bool GetFile(string ftpFolder, string ftpFileName, string localFolder, string localFileName)
    {
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    ftp.Get(ftpFileName, localFolder, localFileName);
    
    return true;
    }
    catch
    {
    try
    {
    ftp.DisConnect();
    ftp = null;
    }
    catch { ftp = null; }
    return false;
    }
    }
    
    /// <summary>
    /// 修改文件
    /// </summary>
    /// <param name="ftpFolder">本地目录</param>
    /// <param name="ftpFileName">本地文件名temp</param>
    /// <param name="localFolder">本地目录</param>
    /// <param name="localFileName">本地文件名</param>
    public bool AddMSCFile(string ftpFolder, string ftpFileName, string localFolder, string localFileName, string BscInfo)
    {
    string sLine = "";
    string sResult = "";
    string path = "获得应用程序所在的完整的路径";
    path = path.Substring(0, path.LastIndexOf("\"));
    try
    {
    FileStream fsFile = new FileStream(ftpFolder + "\" + ftpFileName, FileMode.Open);
    FileStream fsFileWrite = new FileStream(localFolder + "\" + localFileName, FileMode.Create);
    StreamReader sr = new StreamReader(fsFile);
    StreamWriter sw = new StreamWriter(fsFileWrite);
    sr.BaseStream.Seek(0, SeekOrigin.Begin);
    while (sr.Peek() > -1)
    {
    sLine = sr.ReadToEnd();
    }
    string[] arStr = sLine.Split(new string[] { "
    " }, StringSplitOptions.RemoveEmptyEntries);
    
    for (int i = 0; i < arStr.Length - 1; i++)
    {
    sResult += BscInfo + "," + arStr[i].Trim() + "
    ";
    }
    sr.Close();
    byte[] connect = new UTF8Encoding(true).GetBytes(sResult);
    fsFileWrite.Write(connect, 0, connect.Length);
    fsFileWrite.Flush();
    sw.Close();
    fsFile.Close();
    fsFileWrite.Close();
    return true;
    }
    catch (Exception e)
    {
    return false;
    }
    }
    
    /// <summary>
    /// 删除文件
    /// </summary>
    /// <param name="ftpFolder">ftp目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    public bool DelFile(string ftpFolder, string ftpFileName)
    {
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    ftp.Delete(ftpFileName);
    return true;
    }
    catch
    {
    return false;
    }
    }
    
    /// <summary>
    /// 上传文件
    /// </summary>
    /// <param name="ftpFolder">ftp目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    public bool PutFile(string ftpFolder, string ftpFileName)
    {
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    ftp.Put(ftpFileName);
    return true;
    }
    catch
    {
    return false;
    }
    }
    
    /// <summary>
    /// 下载文件
    /// </summary>
    /// <param name="ftpFolder">ftp目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    /// <param name="localFolder">本地目录</param>
    /// <param name="localFileName">本地文件名</param>
    public bool GetFileNoBinary(string ftpFolder, string ftpFileName, string localFolder, string localFileName)
    {
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    ftp.GetNoBinary(ftpFileName, localFolder, localFileName);
    return true;
    }
    catch
    {
    try
    {
    ftp.DisConnect();
    ftp = null;
    }
    catch
    {
    ftp = null;
    }
    return false;
    }
    }
    
    /// <summary>
    /// 得到FTP上文件信息
    /// </summary>
    /// <param name="ftpFolder">FTP目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    public string GetFileInfo(string ftpFolder, string ftpFileName)
    {
    string strResult = "";
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (ftp.Connected) ftp.DisConnect();
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    strResult = ftp.GetFileInfo(ftpFileName);
    return strResult;
    }
    catch
    {
    return "";
    }
    }
    
    /// <summary>
    /// 测试FTP服务器是否可登陆
    /// </summary>
    public bool CanConnect()
    {
    if (ftp == null) ftp = this.getFtpClient();
    try
    {
    ftp.Connect();
    ftp.DisConnect();
    return true;
    }
    catch
    {
    return false;
    }
    }
    
    /// <summary>
    /// 得到FTP上文件信息
    /// </summary>
    /// <param name="ftpFolder">FTP目录</param>
    /// <param name="ftpFileName">ftp文件名</param>
    public string GetFileInfoConnected(string ftpFolder, string ftpFileName)
    {
    string strResult = "";
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    strResult = ftp.GetFileInfo(ftpFileName);
    return strResult;
    }
    catch
    {
    return "";
    }
    }
    
    /// <summary>
    /// 得到文件列表
    /// </summary>
    /// <param name="ftpFolder">FTP目录</param>
    /// <returns>FTP通配符号</returns>
    public string[] GetFileList(string ftpFolder, string strMask)
    {
    string[] strResult;
    try
    {
    if (ftp == null) ftp = this.getFtpClient();
    if (!ftp.Connected)
    {
    ftp.Connect();
    ftp.ChDir(ftpFolder);
    }
    strResult = ftp.Dir(strMask);
    return strResult;
    }
    catch
    {
    return null;
    }
    }
    
    /// <summary>
    ///得到FTP传输对象
    /// </summary>
    public FTPClient getFtpClient()
    {
    FTPClient ft = new FTPClient();
    ft.RemoteHost = this.Server;
    ft.RemoteUser = this.User;
    ft.RemotePass = this.Pass;
    return ft;
    }
    }
    }
    View Code
    
    
     1 using System;
      2 using System.Net;
      3 using System.IO;
      4 using System.Text;
      5 using System.Net.Sockets;
      6 using System.Threading;
      7 
      8 namespace DotNet.Dal
      9 {
     10 public class FTPClient
     11 {
     12 public static object obj = new object();
     13 
     14 #region 构造函数
     15 /// <summary>
     16 /// 缺省构造函数
     17 /// </summary>
     18 public FTPClient()
     19 {
     20 strRemoteHost = "";
     21 strRemotePath = "";
     22 strRemoteUser = "";
     23 strRemotePass = "";
     24 strRemotePort = 21;
     25 bConnected = false;
     26 }
     27 
     28 /// <summary>
     29 /// 构造函数
     30 /// </summary>
     31 public FTPClient(string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort)
     32 {
     33 strRemoteHost = remoteHost;
     34 strRemotePath = remotePath;
     35 strRemoteUser = remoteUser;
     36 strRemotePass = remotePass;
     37 strRemotePort = remotePort;
     38 Connect();
     39 }
     40 #endregion
     41 
     42 #region 字段
     43 private int strRemotePort;
     44 private Boolean bConnected;
     45 private string strRemoteHost;
     46 private string strRemotePass;
     47 private string strRemoteUser;
     48 private string strRemotePath;
     49 
     50 /// <summary>
     51 /// 服务器返回的应答信息(包含应答码)
     52 /// </summary>
     53 private string strMsg;
     54 /// <summary>
     55 /// 服务器返回的应答信息(包含应答码)
     56 /// </summary>
     57 private string strReply;
     58 /// <summary>
     59 /// 服务器返回的应答码
     60 /// </summary>
     61 private int iReplyCode;
     62 /// <summary>
     63 /// 进行控制连接的socket
     64 /// </summary>
     65 private Socket socketControl;
     66 /// <summary>
     67 /// 传输模式
     68 /// </summary>
     69 private TransferType trType;
     70 /// <summary>
     71 /// 接收和发送数据的缓冲区
     72 /// </summary>
     73 private static int BLOCK_SIZE = 512;
     74 /// <summary>
     75 /// 编码方式
     76 /// </summary>
     77 Encoding ASCII = Encoding.ASCII;
     78 /// <summary>
     79 /// 字节数组
     80 /// </summary>
     81 Byte[] buffer = new Byte[BLOCK_SIZE];
     82 #endregion
     83 
     84 #region 属性
     85 /// <summary>
     86 /// FTP服务器IP地址
     87 /// </summary>
     88 public string RemoteHost
     89 {
     90 get
     91 {
     92 return strRemoteHost;
     93 }
     94 set
     95 {
     96 strRemoteHost = value;
     97 }
     98 }
     99 
    100 /// <summary>
    101 /// FTP服务器端口
    102 /// </summary>
    103 public int RemotePort
    104 {
    105 get
    106 {
    107 return strRemotePort;
    108 }
    109 set
    110 {
    111 strRemotePort = value;
    112 }
    113 }
    114 
    115 /// <summary>
    116 /// 当前服务器目录
    117 /// </summary>
    118 public string RemotePath
    119 {
    120 get
    121 {
    122 return strRemotePath;
    123 }
    124 set
    125 {
    126 strRemotePath = value;
    127 }
    128 }
    129 
    130 /// <summary>
    131 /// 登录用户账号
    132 /// </summary>
    133 public string RemoteUser
    134 {
    135 set
    136 {
    137 strRemoteUser = value;
    138 }
    139 }
    140 
    141 /// <summary>
    142 /// 用户登录密码
    143 /// </summary>
    144 public string RemotePass
    145 {
    146 set
    147 {
    148 strRemotePass = value;
    149 }
    150 }
    151 
    152 /// <summary>
    153 /// 是否登录
    154 /// </summary>
    155 public bool Connected
    156 {
    157 get
    158 {
    159 return bConnected;
    160 }
    161 }
    162 #endregion
    163 
    164 #region 链接
    165 /// <summary>
    166 /// 建立连接 
    167 /// </summary>
    168 public void Connect()
    169 {
    170 lock (obj)
    171 {
    172 socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    173 IPEndPoint ep = new IPEndPoint(IPAddress.Parse(RemoteHost), strRemotePort);
    174 try
    175 {
    176 socketControl.Connect(ep);
    177 }
    178 catch (Exception)
    179 {
    180 throw new IOException("不能连接ftp服务器");
    181 }
    182 }
    183 ReadReply();
    184 if (iReplyCode != 220)
    185 {
    186 DisConnect();
    187 throw new IOException(strReply.Substring(4));
    188 }
    189 SendCommand("USER " + strRemoteUser);
    190 if (!(iReplyCode == 331 || iReplyCode == 230))
    191 {
    192 CloseSocketConnect();
    193 throw new IOException(strReply.Substring(4));
    194 }
    195 if (iReplyCode != 230)
    196 {
    197 SendCommand("PASS " + strRemotePass);
    198 if (!(iReplyCode == 230 || iReplyCode == 202))
    199 {
    200 CloseSocketConnect();
    201 throw new IOException(strReply.Substring(4));
    202 }
    203 }
    204 bConnected = true;
    205 ChDir(strRemotePath);
    206 }
    207 
    208 /// <summary>
    209 /// 关闭连接
    210 /// </summary>
    211 public void DisConnect()
    212 {
    213 if (socketControl != null)
    214 {
    215 SendCommand("QUIT");
    216 }
    217 CloseSocketConnect();
    218 }
    219 #endregion
    220 
    221 #region 传输模式
    222 /// <summary>
    223 /// 传输模式:二进制类型、ASCII类型
    224 /// </summary>
    225 public enum TransferType { Binary, ASCII };
    226 
    227 /// <summary>
    228 /// 设置传输模式
    229 /// </summary>
    230 /// <param name="ttType">传输模式</param>
    231 public void SetTransferType(TransferType ttType)
    232 {
    233 if (ttType == TransferType.Binary)
    234 {
    235 SendCommand("TYPE I");//binary类型传输
    236 }
    237 else
    238 {
    239 SendCommand("TYPE A");//ASCII类型传输
    240 }
    241 if (iReplyCode != 200)
    242 {
    243 throw new IOException(strReply.Substring(4));
    244 }
    245 else
    246 {
    247 trType = ttType;
    248 }
    249 }
    250 
    251 /// <summary>
    252 /// 获得传输模式
    253 /// </summary>
    254 /// <returns>传输模式</returns>
    255 public TransferType GetTransferType()
    256 {
    257 return trType;
    258 }
    259 #endregion
    260 
    261 #region 文件操作
    262 /// <summary>
    263 /// 获得文件列表
    264 /// </summary>
    265 /// <param name="strMask">文件名的匹配字符串</param>
    266 public string[] Dir(string strMask)
    267 {
    268 if (!bConnected)
    269 {
    270 Connect();
    271 }
    272 Socket socketData = CreateDataSocket();
    273 SendCommand("NLST " + strMask);
    274 if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226))
    275 {
    276 throw new IOException(strReply.Substring(4));
    277 }
    278 strMsg = "";
    279 Thread.Sleep(2000);
    280 while (true)
    281 {
    282 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
    283 strMsg += ASCII.GetString(buffer, 0, iBytes);
    284 if (iBytes < buffer.Length)
    285 {
    286 break;
    287 }
    288 }
    289 char[] seperator = { '
    ' };
    290 string[] strsFileList = strMsg.Split(seperator);
    291 socketData.Close(); //数据socket关闭时也会有返回码
    292 if (iReplyCode != 226)
    293 {
    294 ReadReply();
    295 if (iReplyCode != 226)
    296 {
    297 
    298 throw new IOException(strReply.Substring(4));
    299 }
    300 }
    301 return strsFileList;
    302 }
    303 
    304 public void newPutByGuid(string strFileName, string strGuid)
    305 {
    306 if (!bConnected)
    307 {
    308 Connect();
    309 }
    310 string str = strFileName.Substring(0, strFileName.LastIndexOf("\"));
    311 string strTypeName = strFileName.Substring(strFileName.LastIndexOf("."));
    312 strGuid = str + "\" + strGuid;
    313 Socket socketData = CreateDataSocket();
    314 SendCommand("STOR " + Path.GetFileName(strGuid));
    315 if (!(iReplyCode == 125 || iReplyCode == 150))
    316 {
    317 throw new IOException(strReply.Substring(4));
    318 }
    319 FileStream input = new FileStream(strGuid, FileMode.Open);
    320 input.Flush();
    321 int iBytes = 0;
    322 while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0)
    323 {
    324 socketData.Send(buffer, iBytes, 0);
    325 }
    326 input.Close();
    327 if (socketData.Connected)
    328 {
    329 socketData.Close();
    330 }
    331 if (!(iReplyCode == 226 || iReplyCode == 250))
    332 {
    333 ReadReply();
    334 if (!(iReplyCode == 226 || iReplyCode == 250))
    335 {
    336 throw new IOException(strReply.Substring(4));
    337 }
    338 }
    339 }
    340 
    341 /// <summary>
    342 /// 获取文件大小
    343 /// </summary>
    344 /// <param name="strFileName">文件名</param>
    345 /// <returns>文件大小</returns>
    346 public long GetFileSize(string strFileName)
    347 {
    348 if (!bConnected)
    349 {
    350 Connect();
    351 }
    352 SendCommand("SIZE " + Path.GetFileName(strFileName));
    353 long lSize = 0;
    354 if (iReplyCode == 213)
    355 {
    356 lSize = Int64.Parse(strReply.Substring(4));
    357 }
    358 else
    359 {
    360 throw new IOException(strReply.Substring(4));
    361 }
    362 return lSize;
    363 }
    364 
    365 
    366 /// <summary>
    367 /// 获取文件信息
    368 /// </summary>
    369 /// <param name="strFileName">文件名</param>
    370 /// <returns>文件大小</returns>
    371 public string GetFileInfo(string strFileName)
    372 {
    373 if (!bConnected)
    374 {
    375 Connect();
    376 }
    377 Socket socketData = CreateDataSocket();
    378 SendCommand("LIST " + strFileName);
    379 string strResult = "";
    380 if (!(iReplyCode == 150 || iReplyCode == 125
    381 || iReplyCode == 226 || iReplyCode == 250))
    382 {
    383 throw new IOException(strReply.Substring(4));
    384 }
    385 byte[] b = new byte[512];
    386 MemoryStream ms = new MemoryStream();
    387 
    388 while (true)
    389 {
    390 int iBytes = socketData.Receive(b, b.Length, 0);
    391 ms.Write(b, 0, iBytes);
    392 if (iBytes <= 0)
    393 {
    394 
    395 break;
    396 }
    397 }
    398 byte[] bt = ms.GetBuffer();
    399 strResult = System.Text.Encoding.ASCII.GetString(bt);
    400 ms.Close();
    401 return strResult;
    402 }
    403 
    404 /// <summary>
    405 /// 删除
    406 /// </summary>
    407 /// <param name="strFileName">待删除文件名</param>
    408 public void Delete(string strFileName)
    409 {
    410 if (!bConnected)
    411 {
    412 Connect();
    413 }
    414 SendCommand("DELE " + strFileName);
    415 if (iReplyCode != 250)
    416 {
    417 throw new IOException(strReply.Substring(4));
    418 }
    419 }
    420 
    421 /// <summary>
    422 /// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)
    423 /// </summary>
    424 /// <param name="strOldFileName">旧文件名</param>
    425 /// <param name="strNewFileName">新文件名</param>
    426 public void Rename(string strOldFileName, string strNewFileName)
    427 {
    428 if (!bConnected)
    429 {
    430 Connect();
    431 }
    432 SendCommand("RNFR " + strOldFileName);
    433 if (iReplyCode != 350)
    434 {
    435 throw new IOException(strReply.Substring(4));
    436 }
    437 // 如果新文件名与原有文件重名,将覆盖原有文件
    438 SendCommand("RNTO " + strNewFileName);
    439 if (iReplyCode != 250)
    440 {
    441 throw new IOException(strReply.Substring(4));
    442 }
    443 }
    444 #endregion
    445 
    446 #region 上传和下载
    447 /// <summary>
    448 /// 下载一批文件
    449 /// </summary>
    450 /// <param name="strFileNameMask">文件名的匹配字符串</param>
    451 /// <param name="strFolder">本地目录(不得以结束)</param>
    452 public void Get(string strFileNameMask, string strFolder)
    453 {
    454 if (!bConnected)
    455 {
    456 Connect();
    457 }
    458 string[] strFiles = Dir(strFileNameMask);
    459 foreach (string strFile in strFiles)
    460 {
    461 if (!strFile.Equals(""))//一般来说strFiles的最后一个元素可能是空字符串
    462 {
    463 Get(strFile, strFolder, strFile);
    464 }
    465 }
    466 }
    467 
    468 /// <summary>
    469 /// 下载一个文件
    470 /// </summary>
    471 /// <param name="strRemoteFileName">要下载的文件名</param>
    472 /// <param name="strFolder">本地目录(不得以结束)</param>
    473 /// <param name="strLocalFileName">保存在本地时的文件名</param>
    474 public void Get(string strRemoteFileName, string strFolder, string strLocalFileName)
    475 {
    476 Socket socketData = CreateDataSocket();
    477 try
    478 {
    479 if (!bConnected)
    480 {
    481 Connect();
    482 }
    483 SetTransferType(TransferType.Binary);
    484 if (strLocalFileName.Equals(""))
    485 {
    486 strLocalFileName = strRemoteFileName;
    487 }
    488 SendCommand("RETR " + strRemoteFileName);
    489 if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226 || iReplyCode == 250))
    490 {
    491 throw new IOException(strReply.Substring(4));
    492 }
    493 FileStream output = new FileStream(strFolder + "\" + strLocalFileName, FileMode.Create);
    494 while (true)
    495 {
    496 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
    497 output.Write(buffer, 0, iBytes);
    498 if (iBytes <= 0)
    499 {
    500 break;
    501 }
    502 }
    503 output.Close();
    504 if (socketData.Connected)
    505 {
    506 socketData.Close();
    507 }
    508 if (!(iReplyCode == 226 || iReplyCode == 250))
    509 {
    510 ReadReply();
    511 if (!(iReplyCode == 226 || iReplyCode == 250))
    512 {
    513 throw new IOException(strReply.Substring(4));
    514 }
    515 }
    516 }
    517 catch
    518 {
    519 socketData.Close();
    520 socketData = null;
    521 socketControl.Close();
    522 bConnected = false;
    523 socketControl = null;
    524 }
    525 }
    526 
    527 /// <summary>
    528 /// 下载一个文件
    529 /// </summary>
    530 /// <param name="strRemoteFileName">要下载的文件名</param>
    531 /// <param name="strFolder">本地目录(不得以结束)</param>
    532 /// <param name="strLocalFileName">保存在本地时的文件名</param>
    533 public void GetNoBinary(string strRemoteFileName, string strFolder, string strLocalFileName)
    534 {
    535 if (!bConnected)
    536 {
    537 Connect();
    538 }
    539 
    540 if (strLocalFileName.Equals(""))
    541 {
    542 strLocalFileName = strRemoteFileName;
    543 }
    544 Socket socketData = CreateDataSocket();
    545 SendCommand("RETR " + strRemoteFileName);
    546 if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226 || iReplyCode == 250))
    547 {
    548 throw new IOException(strReply.Substring(4));
    549 }
    550 FileStream output = new FileStream(strFolder + "\" + strLocalFileName, FileMode.Create);
    551 while (true)
    552 {
    553 int iBytes = socketData.Receive(buffer, buffer.Length, 0);
    554 output.Write(buffer, 0, iBytes);
    555 if (iBytes <= 0)
    556 {
    557 break;
    558 }
    559 }
    560 output.Close();
    561 if (socketData.Connected)
    562 {
    563 socketData.Close();
    564 }
    565 if (!(iReplyCode == 226 || iReplyCode == 250))
    566 {
    567 ReadReply();
    568 if (!(iReplyCode == 226 || iReplyCode == 250))
    569 {
    570 throw new IOException(strReply.Substring(4));
    571 }
    572 }
    573 }
    574 
    575 /// <summary>
    576 /// 上传一批文件
    577 /// </summary>
    578 /// <param name="strFolder">本地目录(不得以结束)</param>
    579 /// <param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param>
    580 public void Put(string strFolder, string strFileNameMask)
    581 {
    582 string[] strFiles = Directory.GetFiles(strFolder, strFileNameMask);
    583 foreach (string strFile in strFiles)
    584 {
    585 Put(strFile);
    586 }
    587 }
    588 
    589 /// <summary>
    590 /// 上传一个文件
    591 /// </summary>
    592 /// <param name="strFileName">本地文件名</param>
    593 public void Put(string strFileName)
    594 {
    595 if (!bConnected)
    596 {
    597 Connect();
    598 }
    599 Socket socketData = CreateDataSocket();
    600 if (Path.GetExtension(strFileName) == "")
    601 SendCommand("STOR " + Path.GetFileNameWithoutExtension(strFileName));
    602 else
    603 SendCommand("STOR " + Path.GetFileName(strFileName));
    604 
    605 if (!(iReplyCode == 125 || iReplyCode == 150))
    606 {
    607 throw new IOException(strReply.Substring(4));
    608 }
    609 
    610 FileStream input = new FileStream(strFileName, FileMode.Open);
    611 int iBytes = 0;
    612 while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0)
    613 {
    614 socketData.Send(buffer, iBytes, 0);
    615 }
    616 input.Close();
    617 if (socketData.Connected)
    618 {
    619 socketData.Close();
    620 }
    621 if (!(iReplyCode == 226 || iReplyCode == 250))
    622 {
    623 ReadReply();
    624 if (!(iReplyCode == 226 || iReplyCode == 250))
    625 {
    626 throw new IOException(strReply.Substring(4));
    627 }
    628 }
    629 }
    630 
    631 
    632 /// <summary>
    633 /// 上传一个文件
    634 /// </summary>
    635 /// <param name="strFileName">本地文件名</param>
    636 public void PutByGuid(string strFileName, string strGuid)
    637 {
    638 if (!bConnected)
    639 {
    640 Connect();
    641 }
    642 string str = strFileName.Substring(0, strFileName.LastIndexOf("\"));
    643 string strTypeName = strFileName.Substring(strFileName.LastIndexOf("."));
    644 strGuid = str + "\" + strGuid;
    645 System.IO.File.Copy(strFileName, strGuid);
    646 System.IO.File.SetAttributes(strGuid, System.IO.FileAttributes.Normal);
    647 Socket socketData = CreateDataSocket();
    648 SendCommand("STOR " + Path.GetFileName(strGuid));
    649 if (!(iReplyCode == 125 || iReplyCode == 150))
    650 {
    651 throw new IOException(strReply.Substring(4));
    652 }
    653 FileStream input = new FileStream(strGuid, FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
    654 int iBytes = 0;
    655 while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0)
    656 {
    657 socketData.Send(buffer, iBytes, 0);
    658 }
    659 input.Close();
    660 File.Delete(strGuid);
    661 if (socketData.Connected)
    662 {
    663 socketData.Close();
    664 }
    665 if (!(iReplyCode == 226 || iReplyCode == 250))
    666 {
    667 ReadReply();
    668 if (!(iReplyCode == 226 || iReplyCode == 250))
    669 {
    670 throw new IOException(strReply.Substring(4));
    671 }
    672 }
    673 }
    674 #endregion
    675 
    676 #region 目录操作
    677 /// <summary>
    678 /// 创建目录
    679 /// </summary>
    680 /// <param name="strDirName">目录名</param>
    681 public void MkDir(string strDirName)
    682 {
    683 if (!bConnected)
    684 {
    685 Connect();
    686 }
    687 SendCommand("MKD " + strDirName);
    688 if (iReplyCode != 257)
    689 {
    690 throw new IOException(strReply.Substring(4));
    691 }
    692 }
    693 
    694 /// <summary>
    695 /// 删除目录
    696 /// </summary>
    697 /// <param name="strDirName">目录名</param>
    698 public void RmDir(string strDirName)
    699 {
    700 if (!bConnected)
    701 {
    702 Connect();
    703 }
    704 SendCommand("RMD " + strDirName);
    705 if (iReplyCode != 250)
    706 {
    707 throw new IOException(strReply.Substring(4));
    708 }
    709 }
    710 
    711 /// <summary>
    712 /// 改变目录
    713 /// </summary>
    714 /// <param name="strDirName">新的工作目录名</param>
    715 public void ChDir(string strDirName)
    716 {
    717 if (strDirName.Equals(".") || strDirName.Equals(""))
    718 {
    719 return;
    720 }
    721 if (!bConnected)
    722 {
    723 Connect();
    724 }
    725 SendCommand("CWD " + strDirName);
    726 if (iReplyCode != 250)
    727 {
    728 throw new IOException(strReply.Substring(4));
    729 }
    730 this.strRemotePath = strDirName;
    731 }
    732 #endregion
    733 
    734 #region 内部函数
    735 /// <summary>
    736 /// 将一行应答字符串记录在strReply和strMsg,应答码记录在iReplyCode
    737 /// </summary>
    738 private void ReadReply()
    739 {
    740 strMsg = "";
    741 strReply = ReadLine();
    742 iReplyCode = Int32.Parse(strReply.Substring(0, 3));
    743 }
    744 
    745 /// <summary>
    746 /// 建立进行数据连接的socket
    747 /// </summary>
    748 /// <returns>数据连接socket</returns>
    749 private Socket CreateDataSocket()
    750 {
    751 SendCommand("PASV");
    752 if (iReplyCode != 227)
    753 {
    754 throw new IOException(strReply.Substring(4));
    755 }
    756 int index1 = strReply.IndexOf('(');
    757 int index2 = strReply.IndexOf(')');
    758 string ipData = strReply.Substring(index1 + 1, index2 - index1 - 1);
    759 int[] parts = new int[6];
    760 int len = ipData.Length;
    761 int partCount = 0;
    762 string buf = "";
    763 for (int i = 0; i < len && partCount <= 6; i++)
    764 {
    765 char ch = Char.Parse(ipData.Substring(i, 1));
    766 if (Char.IsDigit(ch))
    767 buf += ch;
    768 else if (ch != ',')
    769 {
    770 throw new IOException("Malformed PASV strReply: " + strReply);
    771 }
    772 if (ch == ',' || i + 1 == len)
    773 {
    774 try
    775 {
    776 parts[partCount++] = Int32.Parse(buf);
    777 buf = "";
    778 }
    779 catch (Exception)
    780 {
    781 throw new IOException("Malformed PASV strReply: " + strReply);
    782 }
    783 }
    784 }
    785 string ipAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
    786 int port = (parts[4] << 8) + parts[5];
    787 Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    788 IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ipAddress), port);
    789 try
    790 {
    791 s.Connect(ep);
    792 }
    793 catch (Exception)
    794 {
    795 throw new IOException("无法连接ftp服务器");
    796 }
    797 return s;
    798 }
    799 
    800 /// <summary>
    801 /// 关闭socket连接(用于登录以前)
    802 /// </summary>
    803 private void CloseSocketConnect()
    804 {
    805 lock (obj)
    806 {
    807 if (socketControl != null)
    808 {
    809 socketControl.Close();
    810 socketControl = null;
    811 }
    812 bConnected = false;
    813 }
    814 }
    815 
    816 /// <summary>
    817 /// 读取Socket返回的所有字符串
    818 /// </summary>
    819 /// <returns>包含应答码的字符串行</returns>
    820 private string ReadLine()
    821 {
    822 lock (obj)
    823 {
    824 while (true)
    825 {
    826 int iBytes = socketControl.Receive(buffer, buffer.Length, 0);
    827 strMsg += ASCII.GetString(buffer, 0, iBytes);
    828 if (iBytes < buffer.Length)
    829 {
    830 break;
    831 }
    832 }
    833 }
    834 char[] seperator = { '
    ' };
    835 string[] mess = strMsg.Split(seperator);
    836 if (strMsg.Length > 2)
    837 {
    838 strMsg = mess[mess.Length - 2];
    839 }
    840 else
    841 {
    842 strMsg = mess[0];
    843 }
    844 if (!strMsg.Substring(3, 1).Equals(" ")) //返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
    845 {
    846 return ReadLine();
    847 }
    848 return strMsg;
    849 }
    850 
    851 /// <summary>
    852 /// 发送命令并获取应答码和最后一行应答字符串
    853 /// </summary>
    854 /// <param name="strCommand">命令</param>
    855 public void SendCommand(String strCommand)
    856 {
    857 lock (obj)
    858 {
    859 Byte[] cmdBytes = Encoding.ASCII.GetBytes((strCommand + "
    ").ToCharArray());
    860 socketControl.Send(cmdBytes, cmdBytes.Length, 0);
    861 Thread.Sleep(500);
    862 ReadReply();
    863 }
    864 }
    865 #endregion
    866 }
    867 }
    View Code

     http://www.cnblogs.com/sufei/archive/2012/12/09/2810197.html

    QQ群:108845298,期待你的加入

  • 相关阅读:
    Appium Android sdk自动化工具安装
    roboframework-ride运行案例时报 Error 267 错误问题
    Flask 编写http接口api及接口自动化测试
    ssh免密码登录快速配置方法
    Ansible 介绍和使用
    Python 递归返回树形菜单JSON串 <flask>
    python学习之路web框架续
    python学习之路web框架续
    python学习之路web框架
    python学习之路前端-Dom
  • 原文地址:https://www.cnblogs.com/jason-davis/p/FTP.html
Copyright © 2020-2023  润新知