Как использовать FTP для перемещения файлов между каталогами?

у меня есть программа, которую нужно переместить файл из одного каталога в другой на 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-сайт (он находится на моем локальном ПК, и uri разрешен на localhost).

можно ли перемещать файлы между такими каталогами, а если нет, то как это возможно?

для решения некоторых вопросов / предложений, которые были подняты:

  1. я могу загрузить тот же файл из исходный каталог, поэтому он определенно существует (что я делаю, так это сначала загружаю файл, а затем перемещаю его куда-то еще).
  2. я могу получить доступ к ftp-сайту из браузера (как исходный, так и целевой каталог)
  3. ftp-сервер работает под моим собственным экземпляром IIS на моей локальной машине.
  4. путь и дела верны и нет никаких специальных символов.

кроме того, я попытался установить путь к каталогу быть:

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

как для исходного, так и для целевого пути - но это тоже не имеет значения.

нашел этой статья, которая, кажется, говорит, что указание места назначения в качестве относительного пути поможет - не представляется возможным указать абсолютный путь в качестве места назначения.

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

9 ответов


MSDN похоже, что ваш путь считается относительным, и поэтому он пытается войти на FTP-сервер, используя предоставленные учетные данные, а затем устанавливает текущий каталог в . Если это не тот же каталог, где ваш файл, вы получите ошибку 550.


имел ту же проблему и нашел другой способ решить проблему:

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; 

    }

я смог заставить это работать, но ТОЛЬКО используя путь, как он существует на сервере, т. е. /DRIVELETTER:/FOLDERNAME/filename в RenameTo = "


Что делать, если у вас есть только абсолютные пути?

хорошо, я наткнулся на этот пост, потому что я получал ту же ошибку. Ответ, похоже, заключается в использовании относительного пути, что не очень хорошо для решения моей проблемы, потому что я получаю пути к папкам как абсолютные строки пути.

решения, которые я придумал на лету, работают, но, мягко говоря, уродливы. Я сделаю это ответом сообщества wiki, и если у кого-то есть лучшее решение, не стесняйтесь редактировать это.

Так как я узнал это, у меня есть 2 решения.

  1. возьмите абсолютный путь от перемещения к пути и преобразуйте его в относительный URL-адрес.

    public static string GetRelativePath(string ftpBasePath, string ftpToPath)
    {
    
        if (!ftpBasePath.StartsWith("/"))
        {
            throw new Exception("Base path is not absolute");
        }
        else
        {
            ftpBasePath =  ftpBasePath.Substring(1);
        }
        if (ftpBasePath.EndsWith("/"))
        {
            ftpBasePath = ftpBasePath.Substring(0, ftpBasePath.Length - 1);
        }
    
        if (!ftpToPath.StartsWith("/"))
        {
            throw new Exception("Base path is not absolute");
        }
        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;
    }
    

в следующем примере кода я попытался со следующими данными, и он работает.

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

У вас есть эти папки, определенные в службе FTP? Работает ли служба FTP? Если ответ на один вопрос нет, вы не можете использовать FTP для перемещения файлов.

попробуйте открыть папку FTP в FTP-клиенте и посмотреть, что произойдет. Если у вас все еще есть ошибка, что-то не так с вашим определением, поскольку служба FTP не видит папку или папка не логически, где вы думаете, что она находится в службе FTP.

вы также можете открыть Диспетчер IIS и посмотрите, как все настроено в FTP.

Что касается других методов перемещения файлов, вы можете легко перейти с пути UNC на другой, если учетная запись, под которой работает программа, имеет соответствующие разрешения для обоих. Путь UNC похож на путь HTTP или FTP, с ударами в противоположном направлении и без указанного протокола.


код выглядит правильно. Таким образом, либо у вас неправильный путь, файл не существует, либо вам нужно уважать регистр (очевидно, Windows не чувствительна к регистру, но Linux, Unix).

вы пытались открыть файл в браузере? Откройте Проводник файлов Windows и введите адрес в строке пути и посмотрите, что вы получите.


U может использовать этот код:

исходное местоположение файла"ftp://example.com/directory1/Somefile.file".

файл для перемещения, " / newDirectory / Somefile.папка."

обратите внимание, что 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;

Я работаю над идентичным типом проекта, попробуйте создать веб-службу, которая может перемещать файлы и работать на сервере, где находятся ваши файлы. Создайте его с параметром для передачи имени файла. При записи файла для начала вы можете добавить значение к имени файла, скажем, PK данных, которые коррелируют с файлом и т. д.