How can FTPClient delete a directory?

2019-01-27 03:36发布

问题:

I want to delete a folder in FTP.

Can FTPClient object delete it?

回答1:

FtpWebRequest provides the Delete action. Here is a piece of code to achieve that :

               FtpWebRequest reqFTP = FtpWebRequest.Create(uri);
                // Credentials and login handling...

                reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;

                string result = string.Empty;
                FtpWebResponse response = reqFTP.GetResponse();
                long size = response.ContentLength;
                Stream datastream = response.GetResponseStream();
                StreamReader sr = new StreamReader(datastream);
                result = sr.ReadToEnd();
                sr.Close();
                datastream.Close();
                response.Close();

It should work on files and directories. Indeed, please check that you have the right permissions.

Also, you could not delete folders while they are not empty. You must traverse them recursively to delete content before.

The exceptions thrown due to right permissions problems are not always very clear...



回答2:

To delete an empty directory, use the RemoveDirectory "method" of the FtpWebRequest:

void DeleteFtpDirectory(string url, NetworkCredential credentials)
{
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
    request.Method = WebRequestMethods.Ftp.RemoveDirectory;
    request.Credentials = credentials;
    request.GetResponse().Close();
}

use it like:

string url = "ftp://ftp.example.com/directory/todelete";
NetworkCredential credentials = new NetworkCredential("username", "password");
DeleteFtpDirectory(url, credentials);

Though it gets a way more complicated, if you need to delete a non-empty directory. There's no support for recursive operations in FtpWebRequest class (or any other FTP implementation in the .NET framework). You have to implement the recursion yourself:

  • List the remote directory
  • Iterate the entries, deleting files and recursing into subdirectories (listing them again, etc.)

Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest. The FtpWebRequest unfortunately does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.

Your options are:

  • Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory. But that can become a performance problem, when you have a large number of entries.
  • You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
  • You use a long directory listing (LIST command = ListDirectoryDetails method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format).
  • In this specific case, you can just try to delete the entry as a file. If deleting fails, try to list the entry as directory. If the listing succeeds, you assume it's a folder and proceed accordingly. Unfortunately some servers do not error, when you try to list a file. They will just return a listing with a single entry for the file.
static void DeleteFtpDirectory(string url, NetworkCredential credentials)
{
    FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
    listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    listRequest.Credentials = credentials;

    List<string> lines = new List<string>();

    using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse())
    using (Stream listStream = listResponse.GetResponseStream())
    using (StreamReader listReader = new StreamReader(listStream))
    {
        while (!listReader.EndOfStream)
        {
            lines.Add(listReader.ReadLine());
        }
    }

    foreach (string line in lines)
    {
        string[] tokens =
            line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
        string name = tokens[8];
        string permissions = tokens[0];

        string fileUrl = url + name;

        if (permissions[0] == 'd')
        {
            DeleteFtpDirectory(fileUrl + "/", credentials);
        }
        else
        {
            FtpWebRequest deleteRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
            deleteRequest.Method = WebRequestMethods.Ftp.DeleteFile;
            deleteRequest.Credentials = credentials;

            deleteRequest.GetResponse();
        }
    }

    FtpWebRequest removeRequest = (FtpWebRequest)WebRequest.Create(url);
    removeRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;
    removeRequest.Credentials = credentials;

    removeRequest.GetResponse();
}

Use it the same way as the previous (flat) implementation.


Or use a 3rd party library that supports recursive operations.

For example with WinSCP .NET assembly you can delete whole directory with a single call to Session.RemoveFiles:

// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
    Protocol = Protocol.Ftp,
    HostName = "example.com",
    UserName = "user",
    Password = "mypassword",
};

using (Session session = new Session())
{
    // Connect
    session.Open(sessionOptions);

    // Delete folder
    session.RemoveFiles("/directory/todelete").Check();
} 

Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.

(I'm the author of WinSCP)



回答3:

The only way I found working is to rely on "WebRequestMethods.Ftp.DeleteFile" and It will give an exception incase of folders or folders with files so I created a new interenal method to deletedirectory recursively here is the code

public void delete(string deleteFile)
            {
                try
                {
                    /* Create an FTP Request */
                    ftpRequest = (FtpWebRequest)WebRequest.Create(host + "/" + deleteFile);
                    /* Log in to the FTP Server with the User Name and Password Provided */
                    ftpRequest.Credentials = new NetworkCredential(user, pass);
                    /* When in doubt, use these options */
                    ftpRequest.UseBinary = true;
                    ftpRequest.UsePassive = true;
                    ftpRequest.KeepAlive = true;
                    /* Specify the Type of FTP Request */
                    ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile;
                    /* Establish Return Communication with the FTP Server */
                    ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
                    /* Resource Cleanup */
                    ftpResponse.Close();
                    ftpRequest = null;
                }
                catch (Exception ex) { 
                    //Console.WriteLine(ex.ToString()); 
                    try
                    {
                        deleteDirectory(deleteFile);
                    }
                    catch { }


                }
                return;
            }

and directory deletion

/* Delete Directory*/
            private void deleteDirectory(string directoryName)
            {
                try
                {
                    //Check files inside 
                    var direcotryChildren  = directoryListSimple(directoryName);
                    if (direcotryChildren.Any() && (!string.IsNullOrWhiteSpace(direcotryChildren[0])))
                    {
                        foreach (var child in direcotryChildren)
                        {
                            delete(directoryName + "/" +  child);
                        }
                    }


                    /* Create an FTP Request */
                    ftpRequest = (FtpWebRequest)WebRequest.Create(host + "/" + directoryName);
                    /* Log in to the FTP Server with the User Name and Password Provided */
                    ftpRequest.Credentials = new NetworkCredential(user, pass);
                    /* When in doubt, use these options */
                    ftpRequest.UseBinary = true;
                    ftpRequest.UsePassive = true;
                    ftpRequest.KeepAlive = true;
                    /* Specify the Type of FTP Request */
                    ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;

                    /* Establish Return Communication with the FTP Server */
                    ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
                    /* Resource Cleanup */
                    ftpResponse.Close();
                    ftpRequest = null;
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                return;
            }

List Direcotry children

/* List Directory Contents File/Folder Name Only */
            public string[] directoryListSimple(string directory)
            {
                try
                {
                    /* Create an FTP Request */
                    ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/" + directory);
                    /* Log in to the FTP Server with the User Name and Password Provided */
                    ftpRequest.Credentials = new NetworkCredential(user, pass);
                    /* When in doubt, use these options */
                    ftpRequest.UseBinary = true;
                    ftpRequest.UsePassive = true;
                    ftpRequest.KeepAlive = true;
                    /* Specify the Type of FTP Request */
                    ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
                    /* Establish Return Communication with the FTP Server */
                    ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
                    /* Establish Return Communication with the FTP Server */
                    ftpStream = ftpResponse.GetResponseStream();
                    /* Get the FTP Server's Response Stream */
                    StreamReader ftpReader = new StreamReader(ftpStream);
                    /* Store the Raw Response */
                    string directoryRaw = null;
                    /* Read Each Line of the Response and Append a Pipe to Each Line for Easy Parsing */
                    try { while (ftpReader.Peek() != -1) { directoryRaw += ftpReader.ReadLine() + "|"; } }
                    catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                    /* Resource Cleanup */
                    ftpReader.Close();
                    ftpStream.Close();
                    ftpResponse.Close();
                    ftpRequest = null;
                    /* Return the Directory Listing as a string Array by Parsing 'directoryRaw' with the Delimiter you Append (I use | in This Example) */
                    try { string[] directoryList = directoryRaw.Split("|".ToCharArray()); return directoryList; }
                    catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                /* Return an Empty string Array if an Exception Occurs */
                return new string[] { "" };
            }

            /* List Directory Contents in Detail (Name, Size, Created, etc.) */
            public string[] directoryListDetailed(string directory)
            {
                try
                {
                    /* Create an FTP Request */
                    ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/" + directory);
                    /* Log in to the FTP Server with the User Name and Password Provided */
                    ftpRequest.Credentials = new NetworkCredential(user, pass);
                    /* When in doubt, use these options */
                    ftpRequest.UseBinary = true;
                    ftpRequest.UsePassive = true;
                    ftpRequest.KeepAlive = true;
                    /* Specify the Type of FTP Request */
                    ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                    /* Establish Return Communication with the FTP Server */
                    ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
                    /* Establish Return Communication with the FTP Server */
                    ftpStream = ftpResponse.GetResponseStream();
                    /* Get the FTP Server's Response Stream */
                    StreamReader ftpReader = new StreamReader(ftpStream);
                    /* Store the Raw Response */
                    string directoryRaw = null;
                    /* Read Each Line of the Response and Append a Pipe to Each Line for Easy Parsing */
                    try { while (ftpReader.Peek() != -1) { directoryRaw += ftpReader.ReadLine() + "|"; } }
                    catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                    /* Resource Cleanup */
                    ftpReader.Close();
                    ftpStream.Close();
                    ftpResponse.Close();
                    ftpRequest = null;
                    /* Return the Directory Listing as a string Array by Parsing 'directoryRaw' with the Delimiter you Append (I use | in This Example) */
                    try { string[] directoryList = directoryRaw.Split("|".ToCharArray()); return directoryList; }
                    catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                /* Return an Empty string Array if an Exception Occurs */
                return new string[] { "" };
            }


回答4:

Important Point

As mentioned above..

you could not delete folders while they are not empty. You must traverse them recursively to delete content before.



回答5:

public void Deletedir(string remoteFolder)
{
    try
    {
        /* Create an FTP Request */
        ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/"+ remoteFolder);
        /* Log in to the FTP Server with the User Name and Password Provided */
        ftpRequest.Credentials = new NetworkCredential(user, pass);
        /* When in doubt, use these options */
        ftpRequest.UseBinary = true;***strong text***
        ftpRequest.UsePassive = true;
        ftpRequest.KeepAlive = true;
        /* Specify the Type of FTP Request */
        ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;
        /* Establish Return Communication with the FTP Server */
        ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
        /* Get the FTP Server's Response Stream */
        ftpStream = ftpResponse.GetResponseStream();
        /* Open a File Stream to Write the Downloaded File */
    }
    catch { }
}

That's the code you can use. And here's how you can use it, for example, on a button click.

private void button5_Click(object sender, EventArgs e)
{
    ftp ftpClient = new ftp(@"SERVICE PROVIDER", "USERNAME", "PASSWORD");
    ftpClient.Deletedir("DIRECTORY U WANT TO DELETE");
}

And just remember that your folder should be EMPTY.