44

The following code is intended to retrieve a file via FTP. However, I'm getting an error with it.

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();
}

The error is:

The remote server returned an error: (550) File unavailable (e.g., file not found, no access)

The file definitely does exist on the remote machine and I am able to perform this ftp manually (i.e. I have permissions). Can anyone tell me why I might be getting this error?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Paul Michaels
  • 16,185
  • 43
  • 146
  • 269

8 Answers8

57

I know this is an old Post but I am adding here for future reference. Here is a solution that I found:

    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");
        }
    }

Updated based upon excellent suggestion by Ilya Kogan

Mark Kram
  • 5,672
  • 7
  • 51
  • 70
  • 5
    Note that you should dispose of IDisposable objects. The easiest way to do this is to use the keyword `using`. – Ilya Kogan Nov 06 '12 at 12:18
  • You are correct, I posted this response when I was rather new to C# – Mark Kram Nov 06 '12 at 16:58
  • 13
    If you're going to use the `WebClient`, rather than the `FtpWebRequest`, you could use its [`DownloadFile`](http://msdn.microsoft.com/en-us/library/system.net.webclient.downloadfile.aspx) method, rather than messing with a `FileStream`, which might be a little easier. There are some things that WebClient can't do, though (such as use `ACTV` rather than `PASV` FTP: `FtpWebRequest.UsePassive = false;`) – Owen Blacker Dec 10 '12 at 16:24
36

Easiest way

The most trivial way to download a binary file from an FTP server using .NET framework is using 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");

Advanced options

Use FtpWebRequest, only if you need a greater control, that WebClient does not offer (like TLS/SSL encryption, progress monitoring, ascii/text transfer mode, resuming transfers, etc). Easy way is to just copy an FTP response stream to FileStream using Stream.CopyTo:

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);
}

Progress monitoring

If you need to monitor a download progress, you have to copy the contents by chunks yourself:

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);
    }
}

For GUI progress (WinForms ProgressBar), see:
FtpWebRequest FTP download with ProgressBar


Downloading folder

If you want to download all files from a remote folder, see
C# Download all files and subdirectories through FTP.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • How did you come up with the number "10240" for the buffer size? – Donald.Record Mar 23 '18 at 20:42
  • @Donald.Record It's a common practice for a file copy buffer to be in a magnitude of few KBs. It should be larger than a disk sector size. And I do not think that larger than 10KB helps anything. Though actually the [`Stream.CopyTo`](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.copyto) uses a buffer of 80 KB. – Martin Prikryl Mar 24 '18 at 06:58
26

This paragraph from the FptWebRequest class reference might be of interest to you:

The URI may be relative or absolute. If the URI is of the form "ftp://contoso.com/%2fpath" (%2f is an escaped '/'), then the URI is absolute, and the current directory is /path. If, however, the URI is of the form "ftp://contoso.com/path", first the .NET Framework logs into the FTP server (using the user name and password set by the Credentials property), then the current directory is set to /path.

Frank Bollack
  • 24,478
  • 5
  • 49
  • 58
  • 1
    Well, for me it was a matter of dealing with non-ASCII characters, like a # was in the URL, they have to be url encoded. – CularBytes Jun 11 '16 at 17:29
3
    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;

    }

I hope it can help you.

Wai Wai
  • 31
  • 3
2

I had the same issue!

My solution was to insert the public_html folder into the download URL.

Real file location on the server:

myhost.com/public_html/myimages/image.png

Web URL:

www.myhost.com/myimages/image.png

Pablo Claus
  • 5,886
  • 3
  • 29
  • 38
Tom
  • 141
  • 1
  • 6
1
   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;
    }
MORFEE89
  • 137
  • 4
0
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverPath);

After this you may use the below line to avoid error..(access denied etc.)

request.Proxy = null;
kleopatra
  • 51,061
  • 28
  • 99
  • 211
0

FYI, Microsoft recommends not using FtpWebRequest for new development:

We don't recommend that you use the FtpWebRequest class for new development. For more information and alternatives to FtpWebRequest, see WebRequest shouldn't be used on GitHub.

The GitHub link directs to this SO page which contains a list of third-party FTP libraries, such as FluentFTP.

fire_water
  • 1,380
  • 1
  • 19
  • 33