如何使用FTP在目录之间移动文件?

23

我有一个需要将文件从FTP服务器的一个目录移动到另一个目录的程序。例如,文件在以下路径中:

ftp://1.1.1.1/MAIN/Dir1

我需要将文件移动到:

ftp://1.1.1.1/MAIN/Dir2
我找到了几篇文章推荐使用Rename命令,所以我尝试了以下操作:
    Uri serverFile = new Uri(“ftp://1.1.1.1/MAIN/Dir1/MyFile.txt");
    FtpWebRequest reqFTP= (FtpWebRequest)FtpWebRequest.Create(serverFile);
    reqFTP.Method = WebRequestMethods.Ftp.Rename;
    reqFTP.UseBinary = true;
    reqFTP.Credentials = new NetworkCredential(ftpUser, ftpPass);
    reqFTP.RenameTo = “ftp://1.1.1.1/MAIN/Dir2/MyFile.txt";

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

但是这似乎不起作用 - 我得到了以下错误:

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

一开始我以为这可能与权限有关,但据我所见,我对整个FTP站点都有权限(它在我的本地PC上,URI解析为localhost)。

是否应该能够像这样在目录之间移动文件?如果不能,如何实现?

针对一些已经提出的要点/建议:

  1. 我可以从源目录下载同样的文件,因此它确实存在(我所做的是先下载文件,然后将其移动到其他位置)。
  2. 我可以从浏览器中访问FTP站点(源和目标目录都可以)。
  3. FTP服务器在我的本机IIS实例下运行。
  4. 路径和大小写正确,没有特殊字符。

此外,我已经尝试将目录路径设置为:

ftp://1.1.1.1/%2fMAIN/Dir1/MyFile.txt

对于源路径和目标路径都是如此 - 但这也没有什么区别。

我找到了这篇文章,它似乎说指定目标路径为相对路径会有帮助 - 似乎不可能将绝对路径指定为目标。

reqFTP.RenameTo = “../Dir2/MyFile.txt";

1
如果您将ftp://1.1.1.1/MAIN/Dir1/MyFile.txt插入浏览器中,它是否有效? - Aaron McIver
路径中是否包含需要转义的特殊字符? - Conrad Frix
请查看最新的编辑,但回答是:是的-它可以从浏览器中工作,不-路径没有特殊字符。 - Paul Michaels
@PaulMichaels 这个问题是怎么解决的?只用了 .Rename 方法吗? :D - Roxy'Pro
@Roxy'Pro - 我怕我的记忆力不太好。考虑到我标记了Vlad的答案为正确,我想至少这让我找到了解决方案。 - Paul Michaels
9个回答

19

我有同样的问题,找到了另一种解决方法:

public string FtpRename( string source, string destination ) {
        if ( source == destination )
            return;

        Uri uriSource = new Uri( this.Hostname + "/" + source ), UriKind.Absolute );
        Uri uriDestination = new Uri( this.Hostname + "/" + destination ), UriKind.Absolute );

        // Do the files exist?
        if ( !FtpFileExists( uriSource.AbsolutePath ) ) {
            throw ( new FileNotFoundException( string.Format( "Source '{0}' not found!", uriSource.AbsolutePath ) ) );
        }

        if ( FtpFileExists( uriDestination.AbsolutePath ) ) {
            throw ( new ApplicationException( string.Format( "Target '{0}' already exists!", uriDestination.AbsolutePath ) ) );
        }

        Uri targetUriRelative = uriSource.MakeRelativeUri( uriDestination );


        //perform rename
        FtpWebRequest ftp = GetRequest( uriSource.AbsoluteUri );
        ftp.Method = WebRequestMethods.Ftp.Rename;
        ftp.RenameTo = Uri.UnescapeDataString( targetUriRelative.OriginalString );

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

        return response.StatusDescription; 

    }

15

MSDN 看起来暗示您的路径是相对的,因此它尝试使用提供的凭据登录到FTP服务器,然后将当前目录设置为 <UserLoginDirectory>/path 目录。如果这不是您的文件所在的相同目录,您将收到一个 550 错误。


查看最新的编辑。我尝试了各种%2f路径的组合,但都没有成功。 - Paul Michaels
我更新了问题 - 看起来你是对的,但无论我尝试什么,我都没能让绝对路径正常工作。 - Paul Michaels
不错的发现。如果这是我的问题,我可能需要一段时间才能弄清楚。 - Dustin Davis
我之所以知道这个,是因为我在为自己创建的东西浏览那些页面(如FTPWebRequest等)。 - vlad
我需要一个类似的功能。但是,目标FTP位置具有不同的凭据,源具有不同的凭据。请问有人能建议我最好怎么做吗? - Amogh Natu
相对路径可以在这里看到:https://stackoverflow.com/questions/30899571/move-file-to-parent-folder-on-ftp - yu yang Jian

3
在下面的代码示例中,我尝试了以下数据,并且可以正常工作。
FTP登录位置为C:\FTP
文件原始位置为C:\FTP\Data\FTP.txt
要移动的文件为C:\FTP\Move\FTP.txt
Uri serverFile = new Uri("ftp://localhost/Data/FTP.txt");
FtpWebRequest reqFTP= (FtpWebRequest)FtpWebRequest.Create(serverFile);
reqFTP.Method = WebRequestMethods.Ftp.Rename;
reqFTP.Credentials = new NetworkCredential(ftpUser, ftpPass);
reqFTP.RenameTo = "../Move/FTP.txt";

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

1
如果您只有绝对路径呢?
好的,我遇到了这篇文章,因为我也遇到了相同的错误。答案似乎是使用相对路径,但这不是解决我的问题的好方法,因为我得到的文件夹路径是绝对路径字符串。
我随意想出的解决方案有效,但非常丑陋。我将把这个变成社群维基答案,如果有更好的解决方案,请随时编辑它。
自从我学会这个以后,我有两种解决方案。
从移动到路径中获取绝对路径,并将其转换为相对URL。 public static string GetRelativePath(string ftpBasePath, string ftpToPath) {
if (!ftpBasePath.StartsWith("/")) { throw new Exception("基本路径不是绝对路径"); } else { ftpBasePath = ftpBasePath.Substring(1); } if (ftpBasePath.EndsWith("/")) { ftpBasePath = ftpBasePath.Substring(0, ftpBasePath.Length - 1); }
if (!ftpToPath.StartsWith("/")) { throw new Exception("目标路径不是绝对路径"); } else { ftpToPath = ftpToPath.Substring(1); } if (ftpToPath.EndsWith("/")) { ftpToPath = ftpToPath.Substring(0, ftpToPath.Length - 1); } string[] arrBasePath = ftpBasePath.Split("/".ToCharArray()); string[] arrToPath = ftpToPath.Split("/".ToCharArray());
int basePathCount = arrBasePath.Count(); int levelChanged = basePathCount; for (int iIndex = 0; iIndex < basePathCount; iIndex++) { if (arrToPath.Count() > iIndex) { if (arrBasePath[iIndex] != arrToPath[iIndex]) { levelChanged = iIndex; break; } } } int HowManyBack = basePathCount - levelChanged; StringBuilder sb = new StringBuilder(); for (int i = 0; i < HowManyBack; i++) { sb.Append("../"); } for (int i = levelChanged; i < arrToPath.Count(); i++) { sb.Append(arrToPath[i]); sb.Append("/"); }
return sb.ToString(); } public static string MoveFile(string ftpuri, string username, string password, string ftpfrompath, string ftptopath, string filename) { string retval = string.Empty;
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftpfrompath + filename); ftp.Method = WebRequestMethods.Ftp.Rename; ftp.Credentials = new NetworkCredential(username, password); ftp.UsePassive = true; ftp.RenameTo = GetRelativePath(ftpfrompath, ftptopath) + filename; Stream requestStream = ftp.GetRequestStream();
FtpWebResponse ftpresponse = (FtpWebResponse)ftp.GetResponse();
Stream responseStream = ftpresponse.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
return reader.ReadToEnd(); }

或者...

  1. 从“ftp from”路径下载文件,上传到“ftp to”路径并从“ftp from”路径中删除。

    public static string SendFile(string ftpuri, string username, string password, string ftppath, string filename, byte[] datatosend)
    {
        if (ftppath.Substring(ftppath.Length - 1) != "/")
        {
            ftppath += "/";
        }
        FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftppath + filename);
        ftp.Method = WebRequestMethods.Ftp.UploadFile;
        ftp.Credentials = new NetworkCredential(username, password);
        ftp.UsePassive = true;
        ftp.ContentLength = datatosend.Length;
        Stream requestStream = ftp.GetRequestStream();
        requestStream.Write(datatosend, 0, datatosend.Length);
        requestStream.Close();
    
        FtpWebResponse ftpresponse = (FtpWebResponse)ftp.GetResponse();
    
        return ftpresponse.StatusDescription;
    }
    public static string DeleteFile(string ftpuri, string username, string password, string ftppath, string filename)
    {
    
        FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftppath + filename);
        ftp.Method = WebRequestMethods.Ftp.DeleteFile;
        ftp.Credentials = new NetworkCredential(username, password);
        ftp.UsePassive = true;
    
        FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
    
        Stream responseStream = response.GetResponseStream();
    
        StreamReader reader = new StreamReader(responseStream);
    
        return reader.ReadToEnd();
    }
    public static string MoveFile(string ftpuri, string username, string password, string ftpfrompath, string ftptopath, string filename)
    {
        string retval = string.Empty;
        byte[] filecontents = GetFile(ftpuri, username, password, ftpfrompath, filename);
        retval += SendFile(ftpuri, username, password, ftptopath, filename, filecontents);
        retval += DeleteFile(ftpuri, username, password, ftpfrompath, filename);
        return retval;
    }
    

1

我已经成功让它工作了,但是只有通过使用服务器上存在的路径才行,即在RenameTo中使用/DRIVELETTER:/FOLDERNAME/filename


0

你在FTP服务中定义了这些文件夹吗?FTP服务是否正在运行?如果两个问题的答案都是否定的,那么你不能使用FTP来移动文件。

尝试在FTP客户端中打开FTP文件夹并查看会发生什么。如果你仍然遇到错误,那么你的定义可能有问题,因为FTP服务无法看到该文件夹或该文件夹不在你认为它在FTP服务中的逻辑位置上。

你也可以打开IIS管理器并查看FTP设置情况。

至于其他移动文件的方法,只要程序运行的帐户对两个路径都有适当的权限,你就可以轻松地从UNC路径移动到另一个路径。UNC路径类似于HTTP或FTP路径,反斜杠方向相反且没有指定协议。


0

代码看起来正确。所以要么你有错误的路径,文件不存在,或者你需要注意大小写(显然Windows不区分大小写,但Linux、Unix是区分大小写的)。

你尝试在浏览器中打开文件了吗?打开Windows文件资源管理器并在路径栏中输入地址,看看会发生什么。


-1

您可以使用以下代码:

文件原始位置为 "ftp://example.com/directory1/Somefile.file"。

要移动的文件为 "/newDirectory/Somefile.file"。

请注意,目标 URL 不需要以 ftp://example.com 开头。

NetworkCredential User = new NetworkCredential("UserName", "password");
FtpWebRequest Wr =FtpWebRequest)FtpWebRequest.Create("ftp://Somwhere.com/somedirectory/Somefile.file");
Wr.UseBinary = true;
Wr.Method = WebRequestMethods.Ftp.Rename;
Wr.Credentials = User;
Wr.RenameTo = "/someotherDirectory/Somefile.file";
back = (FtpWebResponse)Wr.GetResponse();
bool Success = back.StatusCode == FtpStatusCode.CommandOK || back.StatusCode == FtpStatusCode.FileActionOK;

-2

我正在开发一个类似的项目,尝试构建一个可以在服务器上运行并能够移动文件的 Web 服务。使用参数传递文件名来构建它。在最初编写文件时,您可能希望将一个值附加到文件名中,例如与文件相关联的数据的 PK 等。


1
这应该是一条注释,而不是答案。 - Austin Henley
@Jester610,你能举个例子解释一下吗? - Pabitra Dash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接