Unable to rename file with ftp methods when current user directory is different from root
Asked Answered
P

2

10

Remark: due to spam prevention mechanizm I was forced to replace the beginning of the Uris from ftp:// to ftp.

I've got following problem. I have to upload file with C# ftp method and afterwards rename it. Easy, right? :)

Ok, let's say my ftp host is like this:

ftp.contoso.com

and after logging in, current directory is set to:

users/name

So, what I'm trying to achieve is to log in, upload file to current directory as file.ext.tmp and after upload is successful, rename the file to file.ext

The whole difficulty is, as I guess, to properly set the request Uri for FtpWebRequest.

MSDN states:

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 UserLoginDirectory/path.

Ok, so I upload file with the following URI:

ftp.contoso.com/file.ext.tmp

Great, the file lands where I wanted it to be: in directory "users/name"

Now, I want to rename the file, so I create web request with following Uri:

ftp.contoso.com/file.ext.tmp

and specify rename to parameter as:

file.ext

and this gives me 550 error: file not found, no permissions, etc.

I traced this in Microsoft Network Monitor and it gave me:

Command: RNFR, Rename from
CommandParameter: /file.ext.tmp
Ftp: Response to Port 53724, '550 File /file.ext.tmp not found'

as if it was looking for the file in the root directory - not in the current directory.

I renamed the file manually using Total Commander and the only difference was that CommandParameter was without the first slash:

CommandParameter: file.ext.tmp

I'm able to successfully rename the file by supplying following absolute URI:

ftp.contoso.com/%2fusers/%2fname/file.ext.tmp

but I don't like this approach, since I would have to know the name of current user's directory. It can probably be done by using WebRequestMethods.Ftp.PrintWorkingDirectory, but it adds extra complexity (calling this method to retrieve directory name, then combining the paths to form proper URI).

What I don't understand is why the URI ftp.contoso.com/file.ext.tmp is good for upload and not for rename? Am I missing something here?

The project is set to .NET 4.0, coded in Visual Studio 2010.

Edit

Ok, I place code snippet.

Please note that ftp host, username and password should be filled out. For this sample to work - that is, produce an error - user directory must be different from root ("pwd"-command should return something different than "/")

class Program
{
    private const string fileName = "test.ext";
    private const string tempFileName = fileName + ".tmp";
    private const string ftpHost = "127.0.0.1";
    private const string ftpUserName = "anonymous";
    private const string ftpPassword = "";
    private const int bufferSize = 524288;

    static void Main(string[] args)
    {
        try
        {
            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);

            if (!File.Exists(path))
                File.WriteAllText(path, "FTP RENAME SAMPLE");

            string requestUri = "ftp://" + ftpHost + "/" + tempFileName;

            //upload

            FtpWebRequest uploadRequest = (FtpWebRequest)WebRequest.Create(requestUri);
            uploadRequest.UseBinary = true;
            uploadRequest.UsePassive = true;
            uploadRequest.Credentials = new NetworkCredential(ftpUserName, ftpPassword);
            uploadRequest.KeepAlive = true;
            uploadRequest.Method = WebRequestMethods.Ftp.UploadFile;

            Stream requestStream = null;
            FileStream localFileStream = null;


            localFileStream = File.OpenRead(path);
            requestStream = uploadRequest.GetRequestStream();
            byte[] buffer = new byte[bufferSize];

            int readCount = localFileStream.Read(buffer, 0, bufferSize);
            long bytesSentCounter = 0;

            while (readCount > 0)
            {
                requestStream.Write(buffer, 0, readCount);
                bytesSentCounter += readCount;
                readCount = localFileStream.Read(buffer, 0, bufferSize);
                System.Threading.Thread.Sleep(100);
            }

            localFileStream.Close();
            requestStream.Close();

            FtpWebResponse response = (FtpWebResponse)uploadRequest.GetResponse();
            FtpStatusCode code = response.StatusCode;
            string description = response.StatusDescription;
            response.Close();

            if (code == FtpStatusCode.ClosingData)
                Console.WriteLine("File uploaded successfully");

            //rename

            FtpWebRequest renameRequest = (FtpWebRequest)WebRequest.Create(requestUri);
            renameRequest.UseBinary = true;
            renameRequest.UsePassive = true;
            renameRequest.Credentials = new NetworkCredential(ftpUserName, ftpPassword);
            renameRequest.KeepAlive = true;
            renameRequest.Method = WebRequestMethods.Ftp.Rename;
            renameRequest.RenameTo = fileName;

            try
            {

                FtpWebResponse renameResponse = (FtpWebResponse)renameRequest.GetResponse();

                Console.WriteLine("Rename OK, status code: {0}, rename status description: {1}", response.StatusCode, response.StatusDescription);

                renameResponse.Close();
            }
            catch (WebException ex)
            {
                Console.WriteLine("Rename failed, status code: {0}, rename status description: {1}", ((FtpWebResponse)ex.Response).StatusCode, 
                    ((FtpWebResponse)ex.Response).StatusDescription);
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        finally
        {
            Console.ReadKey();
        }
    }
}
Pascual answered 14/6, 2010 at 7:32 Comment(1)
You gave an extensive coverage of your problem (maybe too extensive?), but what would really help is to show your code, specifically the part where you rename. Note that normally you should not need to prefix the site name when doing FTP.Deaton
M
8

I have encountered a similar issue. The problem is that FtpWebRequest (incorrectly) prepends '/' to rename requests, as can be seen from this log (upload & rename):

URL: 
  http://127.0.0.1/Test.txt
FTP log:
  STOR Test.txt.part
  RNFR /Test.txt.part
  RNTO /Test.txt

Please note that this problem occurs only when you are uploading to the root directory. If you changed the URL to http://127.0.0.1/path/Test.txt, then everything would work fine.

My solution to this problem is to use %2E (dot) as the path:

URL:
  http://127.0.0.1/%2E/Test.txt
FTP log:
 STOR ./Test.txt.part
 RNFR ./Test.txt.part
 RNTO ./Test.txt

You have to url-encode the dot, otherwise FtpWebRequest would simplify the path "/./" to "/".

Marigraph answered 21/2, 2012 at 10:19 Comment(1)
Thank you. This fixed a rather obscure .NET FTP issue for me. No other solutions worked.Normannormand
Y
4

C#

using System.Net;
using System.IO;

Rename Filename on FTP Server function

C#

 private void RenameFileName(string currentFilename, string newFilename)
   {
       FTPSettings.IP = "DOMAIN NAME";
       FTPSettings.UserID = "USER ID";
       FTPSettings.Password = "PASSWORD";
       FtpWebRequest reqFTP = null;
       Stream ftpStream = null ;
       try
       {

           reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + FTPSettings.IP + "/" + currentFilename));
           reqFTP.Method = WebRequestMethods.Ftp.Rename;
           reqFTP.RenameTo = newFilename;
           reqFTP.UseBinary = true;
           reqFTP.Credentials = new NetworkCredential(FTPSettings.UserID, FTPSettings.Password);
           FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
           ftpStream = response.GetResponseStream();
           ftpStream.Close();
           response.Close();
       }
       catch (Exception ex)
       {
           if (ftpStream != null)
           {
               ftpStream.Close();
               ftpStream.Dispose();
           }
           throw new Exception(ex.Message.ToString());
       }
   }

   public static class FTPSettings
   {
       public static string IP { get; set; }
       public static string UserID { get; set; }
       public static string Password { get; set; }
   }
Yabber answered 21/5, 2011 at 7:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.