2

I've been struggling with that for the past few hours and I literally can't find a way to do it without downloading and uploading the file again. Is it even possible?

This question is kind of a copy of this one: How can I use FTP to move files between directories? but I have the feeling that its not solved, although it's answered.

Renaming the file itself is quite easy and works without any problems, but how do I move it to another directory?

I have this sample code:

string uri2 = "ftp://ftpUser@testFtp.com/mainFolder/moveFrom/file.txt";

f = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri2));

f.Credentials = new NetworkCredential(ftpUser, ftpPass);
f.KeepAlive = false;
f.UsePassive = true;
f.Method = WebRequestMethods.Ftp.Rename;
f.Timeout = 5000;
f.UseBinary = true;

f.RenameTo = "ftp://ftpUser@testFtp.com/mainFolder/moveTo/file.txt";

FtpWebResponse response = (FtpWebResponse)f.GetResponse();

response.Close();
f.Abort();

I get the same error as in the other topic:

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

Using a relative path, doesn't do anything different.

Do I do something wrong or is the only way to download from the source folder, upload to dest and then delete the file from the source? That's 3 calls to the FTP server..

Community
  • 1
  • 1
Apostrofix
  • 2,140
  • 8
  • 44
  • 71

1 Answers1

3

.NET puts an abstraction layer over the FTP protocol which can abstract too much away and thus makes some things impossible or tricky. I did not have a source code of .NET, but based on the source of FTPWebRequest from Mono (an implementation which tries to be compatible with .NET) it will do for any operations on URIs:

  • Split first the URI into a directory part and a file_name part (see CWDAndSetFileName in source).
  • CWD to the directory.
  • And do the operation there.

With renaming URL=ftp:/host/foo/bar/file1 to RenameTo=file2 this means in FTP commands:

... log into host ...
CWD foo/bar
RNFR file1
RNTO file2

But, if you have source and target in different directories, e.g. RenameTo=/foo/bar2/file2, you would rather have it like this:

... log into host ...
RNFR foo/bar/file1
RNTO foo/bar2/file2

But unfortunately that is not the way it is implemented :(

If you are lucky you could try to use a relative path, e.g. RenameTo=../bar2/file2, which should result in

... log into host ...
CWD foo/bar
RNFR file1
RNTO ../bar2/file2

This should probably work with the Mono-Implementation, but I don't know if it also works with real .NET.

Edit: I just had a look at the source for .NET 4.5. It differs from Mono in that it does not do a CWD to the basedir before the file operation, but instead prepends the current basedir to the name if the name is relative. Thus with renameTo=../bar2/file2 this results in

 RNFR foo/bar/file1
 RNTO foo/bar/../bar2/file2

while with an absolute name renameTo=/bar2/file2 this would result in

 RNFR foo/bar/file1
 RNTO /bar2/file2

Depending on type and setup of the FTP server the absolute name might get interpreted as relative to the root of the users home directory, or it might be a real absolute path. So it would be best to work only with relative path and the solution with ../dir/ should work with Mono and "real" .NET.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • 1
    I just tested the relative path suggestion from @steffen on .NET 4.0, and it worked. It resulted in the following communication with the FTP server (Sniffed with Wireshark): [RNFR pub/test1/testfile.txt] [350 File exists, ready for destination name] [RNTO pub/test1/../test2/testfile3.txt] [250 RNTO command successful.] So the file was moved AND renamed. – Lars Lind Nilsson Oct 23 '14 at 15:55
  • @Steffen Thank you for the detailed answer! I will look into it and get back to you. Do you have sample code? – Apostrofix Oct 24 '14 at 07:25
  • @Apostrofix: No, I don't write C# myself, I only can read it and I know the FTP protocol. But it should probably just be `f.RenameTo="../moveTo/file.txt"` in your example. – Steffen Ullrich Oct 24 '14 at 07:41