65.9K
CodeProject 正在变化。 阅读更多。
Home

C#.NET 2.0 简单 FTP 演示应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (82投票s)

2007 年 1 月 17 日

7分钟阅读

viewsIcon

794064

downloadIcon

41834

一篇演示使用 C#.NET 2.0 实现常见 FTP 功能的文章。

Sample Image - maximum width is 600 pixels

引言

Microsoft .NET Framework 2.0 相对于 1.x 的一个新增功能是支持 FTP。在此之前,我们不得不依赖第三方库,这些库在大多数情况下都能很好地满足我们的需求,但使用 .NET Framework 库类无疑会带来额外的乐趣。本文提供的代码并非设计为一个功能齐全的可重用库,而是易于使用且可重用的代码片段,它们易于理解,并且可以被重用和修改以适应您的特定需求。因此,每个功能(上传、下载、删除等)的代码都可以单独提取并重用。本文的主要动机是找不到 .NET 2.0 的 FTP 示例代码及其在 C# 中的使用方法;也许是因为它是 .NET 生态系统中的一个新成员,或者现有的第三方实现运行良好,以至于 .NET 2.0 库的这一领域没有得到足够的关注。

背景

我开始将这个 FTP 模块作为我官方工作的一部分,但需求很快发生了变化,我不得不将其改为 .NET 1.1。所以,我没有深入研究。但我相信这能为使用 .NET 2.0 中的 FTP 支持提供一个良好、即时的起点。

Using the Code

别忘了添加以下指令

using System.Net;
using System.IO;

以下步骤可视为使用 FtpWebRequest 对象执行 FTP 请求的通用过程

  1. 在 FTP 服务器 URI 上创建一个 FtpWebRequest 对象
  2. 设置要执行的 FTP 方法(上传下载等)
  3. 为 FTP webrequest 设置选项(SSL 支持、传输模式为二进制/非二进制等)
  4. 设置登录凭据(用户名密码
  5. 执行请求
  6. 接收响应流(如果需要)
  7. 关闭 FTP 请求,以及任何打开的流

在编写任何 FTP 应用程序时需要注意的一点是,要正确设置 FTP 请求的参数以适应 FTP 服务器及其特定配置。FtpWebRequest 对象公开了许多属性来设置这些参数。

上传功能的示例代码如下

首先,创建一个 URI,它代表 FTP 地址以及文件名(包括目录结构)。此 URI 用于创建 FtpWebRequest 实例。

然后,设置 FtpWebRequest 对象的属性,这些属性决定了 FTP 请求的设置。它的一些重要属性包括

  • Credentials - 指定登录 FTP 服务器的用户名和密码。
  • KeepAlive - 指定请求完成后是否关闭控制连接。默认情况下,设置为 true
  • UseBinary - 表示文件传输的数据类型。这里有两种文件传输模式:Binary(二进制)和 ASCII。在比特级别上,两者在字节的第 8 位上有所不同。ASCII 使用第 8 位作为无意义位进行错误控制,而对于二进制,所有 8 位都具有意义。因此,在选择 ASCII 传输时要小心。简单来说,所有能在记事本中打开并正常读取的文件都可以安全地视为 ASCII。可执行文件、格式化文档等应使用二进制模式发送。顺便说一下,将 ASCII 文件作为二进制文件发送在大多数情况下都可以正常工作。
  • UsePassive - 指定使用主动模式或被动模式。以前,主动 FTP 可以与所有客户端正常工作,但现在,由于大多数随机端口都会被防火墙阻止,主动模式可能会失败。在这种情况下,被动 FTP 会有所帮助。但它仍然会在服务器端引起问题。客户端在服务器上请求的较高端口也可能被防火墙阻止。但是,由于 FTP 服务器需要使其服务器可供尽可能多的客户端访问,因此它们几乎肯定需要支持被动 FTP。被动模式被认为是安全的,原因在于它确保所有数据流的启动都来自网络内部(客户端),而不是来自外部(服务器)。
  • Contentlength - 设置此属性对我们请求的服务器很有用,但对我们(客户端)来说用处不大,因为 FtpWebRequest 通常会忽略此属性值,因此在大多数情况下我们无法使用它。但是,如果我们设置了这个属性,FTP 服务器将提前知道它应该预期的文件大小(在上传的情况下)。
  • Method - 表示当前请求要执行的操作(命令)(上传下载filelist 等)。它被设置为 WebRequestMethods.Ftp 结构中定义的值。
private void Upload(string filename)
{
  FileInfo fileInf = new FileInfo(filename);
  string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
  FtpWebRequest reqFTP;
    
  // Create FtpWebRequest object from the Uri provided
  reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(
            "ftp://" + ftpServerIP + "/" + fileInf.Name));

  // Provide the WebPermission Credentials
  reqFTP.Credentials = new NetworkCredential(ftpUserID, 
                                             ftpPassword);
    
  // By default KeepAlive is true, where the control connection is 
  // not closed after a command is executed.
  reqFTP.KeepAlive = false;

  // Specify the command to be executed.
  reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
    
  // Specify the data transfer type.
  reqFTP.UseBinary = true;

  // Notify the server about the size of the uploaded file
  reqFTP.ContentLength = fileInf.Length;

  // The buffer size is set to 2kb
  int buffLength = 2048;
  byte[] buff = new byte[buffLength];
  int contentLen;
    
  // Opens a file stream (System.IO.FileStream) to read 
  the file to be uploaded
  FileStream fs = fileInf.OpenRead();
   
  try
  {
        // Stream to which the file to be upload is written
        Stream strm = reqFTP.GetRequestStream();
        
        // Read from the file stream 2kb at a time
        contentLen = fs.Read(buff, 0, buffLength);
        
        // Till Stream content ends
        while (contentLen != 0)
        {
            // Write Content from the file stream to the 
            // FTP Upload Stream
            strm.Write(buff, 0, contentLen);
            contentLen = fs.Read(buff, 0, buffLength);
        }
        
        // Close the file stream and the Request Stream
        strm.Close();
        fs.Close();
  }
  catch(Exception ex)
    {
        MessageBox.Show(ex.Message, "Upload Error");
    }
}

上面是 FTP 上传(PUT)的示例代码。底层使用的子命令是 STOR。这里,为 FTP 服务器上指定的创建了 FtpWebRequest 对象。设置了请求的各种属性,即 CredentialsKeepAliveMethodUseBinaryContentLength。打开本地机器上的文件,并将内容写入 FTP 请求流。这里使用 2KB 的缓冲区大小,这是适合上传大文件或小文件的合适大小。

private void Download(string filePath, string fileName)
{
    FtpWebRequest reqFTP;
    try
    {
        //filePath = <<The full path where the 
        //file is to be created. the>>, 
        //fileName = <<Name of the file to be createdNeed not 
        //name on FTP server. name name()>>
        FileStream outputStream = new FileStream(filePath + 
                                "\\" + fileName, FileMode.Create);

        reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + 
                                ftpServerIP + "/" + fileName));
        reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
        reqFTP.UseBinary = true;
        reqFTP.Credentials = new NetworkCredential(ftpUserID, 
                                                    ftpPassword);
        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)
    {
        MessageBox.Show(ex.Message);
    }
}        

上面是从 FTP 服务器下载文件的示例代码。与上面描述的 Upload 功能不同,Download 需要响应流,该流将包含所请求文件的内容。
在这里,要下载的文件作为 URI 的一部分指定,该 URI 又用于创建 FtpWebRequest 对象。要“GET”请求的文件,请使用 GetResponse() 方法获取 FtpWebRequest 对象的响应。这个新构建的响应对象提供了包含文件内容作为流的响应流,您可以轻松地将其转换为文件流以在本地获取文件。
注意:我们可以灵活地设置文件在本地机器上保存的位置和名称。

public string[] GetFileList()
{
    string[] downloadFiles;
    StringBuilder result = new StringBuilder();
    FtpWebRequest reqFTP;
    try
    {
        reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(
                  "ftp://" + ftpServerIP + "/"));
        reqFTP.UseBinary = true;
        reqFTP.Credentials = new NetworkCredential(ftpUserID, 
                                                   ftpPassword);
        reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
        WebResponse response = reqFTP.GetResponse();
        StreamReader reader = new StreamReader(response
                                        .GetResponseStream());
        
        string line = reader.ReadLine();
        while (line != null)
        {
            result.Append(line);
            result.Append("\n");
            line = reader.ReadLine();
        }
        // to remove the trailing '\n'
        result.Remove(result.ToString().LastIndexOf('\n'), 1);
        reader.Close();
        response.Close();
        return result.ToString().Split('\n');
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
        downloadFiles = null;
        return downloadFiles;
    }
} 

上面是获取 FTP 服务器上文件列表的示例代码块。URI 构建时指定了 FTP 服务器地址/名称以及所需的路径(如果有)。在上面的示例中,指定了根文件夹来创建 FtpWebRequest 对象。这里,响应流用于创建 StreamReader 对象,该对象具有服务器上所有文件名列表,由“\r\n”(回车符和换行符组合)分隔。您可以使用 StreamReader 对象的 ReadToEnd() 方法获取整个文件列表(以“\r\n”分隔)。上面的实现读取每个文件名,并通过追加每个文件名来创建一个 StringBuilder 对象。生成的 StringBuilder 对象被拆分为一个 string 数组并返回。我相信有更好的方法可以做到这一点。更好的方法可能是从整个列表(由<<StreamReader>>.ReadToEnd()返回)中删除所有‘\r’实例,然后使用‘\n’分隔符分割结果字符串。无论如何,我不想在这上面花费更多的时间和精力 ;-)。

Rename(重命名)、Delete(删除)、GetFileSize(获取文件大小)、FileListDetails(获取文件列表详细信息)、MakeDir(创建目录)的实现与上述代码片段非常相似,并且所附代码易于理解。

注意:对于重命名,新名称可以分配给 FtpWebRequest 对象的 RenameTo 属性。对于 MakeDirectory,新目录的名称可以指定为用于创建 FtpWebRequest 对象的 URI 的一部分。

关注点

在这一领域进行编码时,请注意以下几点

  • 除非 EnableSsl 属性设置为 true,否则所有数据和命令,包括您的用户名和密码信息,都将以明文形式发送到服务器。任何监控网络流量的人都可以看到您的凭据并使用它们连接到服务器。如果您连接到需要凭据并支持安全套接字层 (SSL) 的 FTP 服务器,则应将 EnableSsl 设置为 true
  • 如果您没有访问 FTP 资源的适当 WebPermission,则会抛出 SecurityException 异常。
  • 通过调用 GetResponse 方法将请求发送到服务器。当请求的操作完成时,将返回一个 FtpWebResponse 对象。FtpWebResponse 对象提供了操作的状态以及从服务器下载的任何数据。即,
    FtpWebResponse 对象的 StatusCode 属性提供 FTP 服务器返回的最新状态码。
    FtpWebResponse 对象的 StatusDescription 属性提供返回的状态码的描述。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.