Невозможно переименовать файл с помощью методов ftp, если текущий каталог пользователя отличается от корневого - PullRequest
9 голосов
/ 14 июня 2010

Примечание: из-за механизма предотвращения спама я был вынужден заменить начало Uris с ftp: // на ftp.

У меня следующая проблема. Я должен загрузить файл с C # ftp методом, а затем переименовать его. Легко, правда? :)

Хорошо, допустим, мой ftp-хост выглядит так:

ftp.contoso.com

и после входа в систему текущий каталог устанавливается на:

Пользователи / имя

Итак, я пытаюсь войти в систему, загрузить файл в текущий каталог как file.ext.tmp и после успешной загрузки переименовать файл в file.ext

Вся сложность заключается, как я полагаю, в правильной установке запроса Uri для FtpWebRequest.

Состояния MSDN:

URI может быть относительным или абсолютным. Если URI имеет форму " ftp: //contoso.com/%2fpath" (% 2f - экранированный '/'), тогда URI является абсолютным, и текущим каталогом является / path. Однако, если URI имеет вид « ftp: //contoso.com/path», сначала .NET Framework входит на FTP-сервер (используя имя пользователя и пароль, установленные свойством Credentials. ), тогда текущим каталогом является UserLoginDirectory / path.

Хорошо, я загружаю файл со следующим URI:

ftp.contoso.com / file.ext.tmp

Отлично, файл находится там, где я хотел: в каталоге "users / name"

Теперь я хочу переименовать файл, поэтому я создаю веб-запрос со следующим Uri:

ftp.contoso.com / file.ext.tmp

и укажите переименование параметра как:

file.ext

и это дает ошибку 550: файл не найден, нет разрешений и т. Д.

Я отследил это в Microsoft Network Monitor, и он дал мне:

Команда: RNFR, Переименовать из
CommandParameter: /file.ext.tmp
Ftp: Ответ на порт 53724, «Файл 550 /file.ext.tmp не найден»

как будто он искал файл в корневом каталоге, а не в текущем каталоге.

Я переименовал файл вручную, используя Total Commander, и единственное отличие заключалось в том, что CommandParameter был без первой косой черты:

CommandParameter: file.ext.tmp

Я могу успешно переименовать файл, указав следующий абсолютный URI:

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

но мне не нравится этот подход, так как мне нужно знать имя каталога текущего пользователя. Вероятно, это можно сделать с помощью WebRequestMethods.Ftp.PrintWorkingDirectory, но это добавляет дополнительную сложность (вызов этого метода для получения имени каталога, затем объединение путей для формирования правильного URI).

Что я не понимаю, так это почему URI ftp.contoso.com/file.ext.tmp подходит для загрузки, а не для переименования? Я что-то здесь упускаю?

Проект настроен на .NET 4.0, кодирован в Visual Studio 2010.

Редактировать

Хорошо, я помещаю фрагмент кода.

Обратите внимание, что FTP-хост, имя пользователя и пароль должны быть заполнены. Чтобы этот пример работал - то есть выдает ошибку - каталог пользователя должен отличаться от корневого (команда "pwd" должна возвращать что-то отличное от "/")

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

Ответы [ 2 ]

8 голосов
/ 21 февраля 2012

Я столкнулся с подобной проблемой. Проблема заключается в том, что FtpWebRequest (неправильно) добавляет «/» для переименования запросов, как видно из этого журнала (загрузка и переименование):

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

Обратите внимание, что эта проблема возникает только при загрузке в корневой каталог. Если вы изменили URL-адрес на http://127.0.0.1/path/Test.txt, то все будет работать нормально.

Мое решение этой проблемы - использовать% 2E (точка) в качестве пути:

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

Вы должны url-кодировать точку, иначе FtpWebRequest упростит путь "/./" к "/".

4 голосов
/ 21 мая 2011

C #

using System.Net;
using System.IO;

Переименование имени файла на функции FTP-сервера

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; }
   }
...