Tuesday, October 26, 2010

Uploading a file using FTP in C#

Uploading a file to an FTP server from a program written in C# can be a tricky task. Even though there are plenty of examples in the Web about how to achieve this, those examples were not working for me, despite careful verification of both, the code and the FTP server settings.

My problem was that I was getting the following error:
The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
Of course I verified that the remote location was there on the server and that the location was writable by my user. Finally, my code was basically copied from an MSDN example, so not much of a chance that anything could go wrong there. After a while I noticed that the problem only happened when I was trying to write to a location different that root, like:
ftp://myserver.com/location/NewFileName.txt
didn't work. On the other hand:
ftp://myserver.com/NewFileName.txt
would work as expected.

So if I were to remove location from the URL, it would work perfectly. After quite a while of looking for the answer, I found a post which described the following solution: you should prepend an escaped slash character (%2F) to the location portion of the URL to make it work. Much to my surprise did this not only work, but it also works for multi-level directory structures, having to prepend this prefix only once.

Your remote location would then look as follows:
ftp://myserver.com/%2Flocation/NewFileName.txt
Here is a rough scrap (not compilable as is!) of the code that I'm using that is finally working as expected:

// obtains the file name in a fully qualified file path,
// e.g. for path=C:\Public\test.txt
//   file name=test.txt
private string GetFileName(string path) { }
// the base URL for the FTP sever, e.g. ftp://myserver.com/
private string _baseUrl;
//  credentials
private string _userId;
private string _password;

public void UploadFile(string localFilePath,
                       string remoteLocation)
{
  // create a request URL, with the URL pointing
  // to the new file's location
  string requestUrl;
  if (remoteLocation != null && remoteLocation.Length > 0)
      requestUrl = _baseUrl + "%2F" + remoteLocation + "/" + GetFileName(localFilePath);
  else
      requestUrl = _baseUrl + GetFileName(localFilePath);

  //  create a web request using that URL
  FtpWebRequest request = (FtpWebRequest)WebRequest.Create(requestUrl);
  request.Method = WebRequestMethods.Ftp.UploadFile;
  request.Credentials = new NetworkCredential(_userId, _password);

  //  read the local file as a byte array
  StreamReader src = new StreamReader(localFilePath);
  byte[] fileContents = Encoding.UTF8.GetBytes(src.ReadToEnd());
  src.Close();
  request.ContentLength = fileContents.Length;

  //  write those bytes out to the request
  Stream requestStream = request.GetRequestStream();
  requestStream.Write(fileContents, 0, fileContents.Length);
  requestStream.Close();

  //  check the server response
  FtpWebResponse response =
    (FtpWebResponse)request.GetResponse();
  response.Close();
}

I hope this helps somebody! :-)
BTW I'm using the .NET framework version 2, and a Windows FTP server.

1 comment:

  1. This is a great FTP Library for C#, it is reasonably priced too:
    https://www.kellermansoftware.com/p-39-net-ftp-library.aspx

    ReplyDelete