FtpWebRequest 下载文件

Posted

技术标签:

【中文标题】FtpWebRequest 下载文件【英文标题】:FtpWebRequest Download File 【发布时间】:2011-02-16 10:07:23 【问题描述】:

以下代码旨在通过 FTP 检索文件。但是,我遇到了错误。

serverPath = "ftp://x.x.x.x/tmp/myfile.txt";

FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverPath);

request.KeepAlive = true;
request.UsePassive = true;
request.UseBinary = true;

request.Method = WebRequestMethods.Ftp.DownloadFile;                
request.Credentials = new NetworkCredential(username, password);

// Read the file from the server & write to destination                
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) // Error here
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))            
using (StreamWriter destination = new StreamWriter(destinationFile))

    destination.Write(reader.ReadToEnd());
    destination.Flush();

错误是:

远程服务器返回错误:(550) 文件不可用(例如,找不到文件,无法访问)

该文件确实存在于远程机器上,我可以手动执行此 ftp(即我有权限)。谁能告诉我为什么会出现这个错误?

【问题讨论】:

我发现wireshark 对这样的东西很有用。您可以设置过滤器来查看您的机器和服务器之间的 FTP 流量。 如果将 UsePassive 设置为 false 会发生什么?我从来没有让任何服务器使用被动模式工作.. 根据我的经验,这通常会导致超时错误,因为它尝试使用被防火墙阻止的端口。 嗯,据我所知,其余代码对我来说似乎没问题。 【参考方案1】:

最简单的方法

使用 .NET 框架从 FTP 服务器下载二进制文件最简单的方法是使用 WebClient.DownloadFile

WebClient client = new WebClient();
client.Credentials = new NetworkCredential("username", "password");
client.DownloadFile(
    "ftp://ftp.example.com/remote/path/file.zip", @"C:\local\path\file.zip");

高级选项

使用FtpWebRequest,仅当您需要更大的控制,WebClient 不提供(如TLS/SSL encryption、进度监控、ascii/文本传输模式、resuming transfers 等)。简单的方法是使用 Stream.CopyTo 将 FTP 响应流复制到 FileStream

FtpWebRequest request =
    (FtpWebRequest)WebRequest.Create("ftp://ftp.example.com/remote/path/file.zip");
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.DownloadFile;

using (Stream ftpStream = request.GetResponse().GetResponseStream())
using (Stream fileStream = File.Create(@"C:\local\path\file.zip"))

    ftpStream.CopyTo(fileStream);


进度监控

如果你需要监控一个下载进度,你必须自己分块复制内容:

FtpWebRequest request =
    (FtpWebRequest)WebRequest.Create("ftp://ftp.example.com/remote/path/file.zip");
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.DownloadFile;

using (Stream ftpStream = request.GetResponse().GetResponseStream())
using (Stream fileStream = File.Create(@"C:\local\path\file.zip"))

    byte[] buffer = new byte[10240];
    int read;
    while ((read = ftpStream.Read(buffer, 0, buffer.Length)) > 0)
    
        fileStream.Write(buffer, 0, read);
        Console.WriteLine("Downloaded 0 bytes", fileStream.Position);
    

有关 GUI 进度 (WinForms ProgressBar),请参阅:FtpWebRequest FTP download with ProgressBar


下载文件夹

如果您想从远程文件夹下载所有文件,请参阅C# Download all files and subdirectories through FTP。

【讨论】:

你是如何得出缓冲区大小的数字“10240”的? @Donald.Record 文件复制缓冲区大小为几 KB 是一种常见做法。它应该大于磁盘扇区大小。而且我不认为大于 10KB 有什么帮助。虽然实际上 Stream.CopyTo 使用了 80 KB 的缓冲区。【参考方案2】:

仅供参考,Microsoft recommends not using FtpWebRequest for new development:

我们不建议您将 FtpWebRequest 类用于新开发。有关 FtpWebRequest 的更多信息和替代方案,请参阅 WebRequest 不应在 GitHub 上使用。

GitHub 链接指向 this SO page,其中包含第三方 FTP 库的列表,例如 FluentFTP。

【讨论】:

【参考方案3】:
   public void download(string remoteFile, string localFile)
    
       private string host = "yourhost";
       private string user = "username";
       private string pass = "passwd";
       private FtpWebRequest ftpRequest = null;
       private FtpWebResponse ftpResponse = null;
       private Stream ftpStream = null;
       private int bufferSize = 2048;

        try
        
            ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/" + remoteFile);

            ftpRequest.Credentials = new NetworkCredential(user, pass);

            ftpRequest.UseBinary = true;
            ftpRequest.UsePassive = true;
            ftpRequest.KeepAlive = true;

            ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
            ftpStream = ftpResponse.GetResponseStream();

            FileStream localFileStream = new FileStream(localFile, FileMode.Create);

            byte[] byteBuffer = new byte[bufferSize];
            int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);

            try
            
                while (bytesRead > 0)
                
                    localFileStream.Write(byteBuffer, 0, bytesRead);
                    bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
                
            

            catch (Exception)   

            localFileStream.Close();
            ftpStream.Close();
            ftpResponse.Close();
            ftpRequest = null;
        

        catch (Exception)   
        return;
    

【讨论】:

【参考方案4】:
    private static DataTable ReadFTP_CSV()
    
        String ftpserver = "ftp://servername/ImportData/xxxx.csv";
        FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpserver));

        reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
        FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();

        Stream responseStream = response.GetResponseStream();

        // use the stream to read file from FTP 
        StreamReader sr = new StreamReader(responseStream);
        DataTable dt_csvFile = new DataTable();

        #region Code
        //Add Code Here To Loop txt or CSV file
        #endregion

        return dt_csvFile;

    

希望对你有帮助。

【讨论】:

【参考方案5】:

我知道这是一篇旧帖子,但我在这里添加以供将来参考。这是我找到的解决方案:

    private void DownloadFileFTP()
    
        string inputfilepath = @"C:\Temp\FileName.exe";
        string ftphost = "xxx.xx.x.xxx";
        string ftpfilepath = "/Updater/Dir1/FileName.exe";

        string ftpfullpath = "ftp://" + ftphost + ftpfilepath;

        using (WebClient request = new WebClient())
        
            request.Credentials = new NetworkCredential("UserName", "P@55w0rd");
            byte[] fileData = request.DownloadData(ftpfullpath);

            using (FileStream file = File.Create(inputfilepath))
            
                file.Write(fileData, 0, fileData.Length);
                file.Close();
            
            MessageBox.Show("Download Complete");
        
    

根据 Ilya Kogan 的出色建议更新

【讨论】:

请注意,您应该处置 IDisposable 对象。最简单的方法是使用关键字using 你说得对,我是在对 C# 比较陌生时发布了这个回复 如果你打算使用WebClient,而不是FtpWebRequest,你可以使用它的DownloadFile方法,而不是使用FileStream,这可能有点更轻松。不过,有些事情 WebClient 不能做(例如使用 ACTV 而不是 PASV FTP:FtpWebRequest.UsePassive = false;【参考方案6】:
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverPath);

在此之后,您可以使用以下行来避免错误..(访问被拒绝等)

request.Proxy = null;

【讨论】:

代理默认什么都不是。【参考方案7】:

我遇到了同样的问题!

我的解决方案是将public_html 文件夹插入到下载网址中。

服务器上的真实文件位置:

myhost.com/public_html/myimages/image.png

网址:

www.myhost.com/myimages/image.png

【讨论】:

【参考方案8】:

FptWebRequest class reference 中的这段话您可能会感兴趣:

URI 可以是相对的或绝对的。 如果 URI 的格式为 “ftp://contoso.com/%2fpath”(%2f 是 一个转义的 '/'),​​那么 URI 是 绝对的,当前目录是 /小路。但是,如果 URI 属于 形成“ftp://contoso.com/path”,首先 .NET Framework 登录到 FTP 服务器(使用用户名和 凭据设置的密码 属性),然后是当前目录 设置为 /path。

【讨论】:

嗯,对我来说,这是处理非 ASCII 字符的问题,就像 URL 中的 # 一样,它们必须进行 url 编码。

以上是关于FtpWebRequest 下载文件的主要内容,如果未能解决你的问题,请参考以下文章

C#:FTP 上传缓冲区大小

为啥 FtpWebRequest 恰好在长传输结束时抛出 WebException?

使用 FtpWebRequest 附加到大型机上的文件时出现问题

FtpWebRequest打开随机端口而不是21

使用 C# 到 FTP 文件到大型机,包括数据集 - 将 FTP 脚本翻译成 FtpWebRequest 代码

FtpWebRequest 使用显式 TLS/SSL