How to get a directories file size from an FTP protocol in a .NET application
Asked Answered
A

3

3

I am currently making a small .NET console application to do an automateed backup of some of my files onto my server. The issue that I am running into is that I've had some bad weather on my end which led to some power and network outages. During this time I noticed that a good portion of my files didn't go through or got corrupt. I was wondering if there was a way to get a size of the folder on the other end and see if the file names, number of files, and total directory size match up. I've tried WinSCP and NcFTP as ways to transfer files over, but I haven't seen anything regarding getting a proper filesize.

This is pretty much a windows to windows transfer so if there is a command line argument that gives me back a size through the FTP client that would be great.

Archbishop answered 25/10, 2011 at 15:26 Comment(0)
F
1

There is no standard way to request "total size of files in this directory". You can ask for each file size individually via SIZE file.txt, or you can ask for ls -l of an entire directory and parse the file sizes out.

Flash answered 25/10, 2011 at 16:58 Comment(1)
This method is tedious for directories with large files and trying to parse it from over the network, but overall this seems to work. Thanks for the help.Archbishop
M
2

There's no standard FTP command to retrieve a directory size.

You have to recursively iterate all subdirectories and files and sum the sizes.

This is not easy with .NET framework/FtpWebRequest, as it does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol.

All you can do is to use LIST command (ListDirectoryDetails) and try to parse a server-specific listing. Many FTP servers use *nix-style listing. But many servers use a different format. The following example uses *nix format:

static long CalculateFtpDirectorySize(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 (var listResponse = (FtpWebResponse)listRequest.GetResponse())
    using (Stream listStream = listResponse.GetResponseStream())
    using (StreamReader listReader = new StreamReader(listStream))
    {
        while (!listReader.EndOfStream)
        {
            lines.Add(listReader.ReadLine());
        }
    }

    long result = 0;
    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')
        {
            result += CalculateFtpDirectorySize(fileUrl + "/", credentials);
        }
        else
        {
            result += long.Parse(tokens[4]);
        }
    }

    return result;
}

Use it like:

var credentials = new NetworkCredential("username", "password");
long size = CalculateFtpDirectorySize("ftp://ftp.example.com/", credentials);

If your server uses DOS/Windows listing format, see C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response


Alternatively you can use a 3rd party FTP client implementation that supports the modern MLSD command.

For example WinSCP .NET assembly supports that.

And it even has handy Session.EnumerateRemoteFiles method, which makes calculating directory size easy task:

var opts = EnumerationOptions.AllDirectories;
var files = session.EnumerateRemoteFiles("/", null, opts);
long size = files.Select(fileInfo => fileInfo.Length).Sum();

A complete code would be like:

SessionOptions sessionOptions = new SessionOptions
{
    Protocol = Protocol.Ftp,
    HostName = "ftp.example.com",
    UserName = "username",
    Password = "password",
};

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

    var opts = EnumerationOptions.AllDirectories;
    var files = session.EnumerateRemoteFiles("/", null, opts);
    long size = files.Select(fileInfo => fileInfo.Length).Sum();
}

(I'm the author of WinSCP)

Maddox answered 4/9, 2017 at 19:29 Comment(0)
F
1

There is no standard way to request "total size of files in this directory". You can ask for each file size individually via SIZE file.txt, or you can ask for ls -l of an entire directory and parse the file sizes out.

Flash answered 25/10, 2011 at 16:58 Comment(1)
This method is tedious for directories with large files and trying to parse it from over the network, but overall this seems to work. Thanks for the help.Archbishop
P
0

I think that your best bet is to obtain a full list of files and then send them one at a time. If the connection fails during transfer then that file upload will fail. If a file upload is successful then you can remove that file name from the list.

as you've mentioned NET in your tags perhaps you should look here for an example of how to perform it in C#. VB.Net will be similar, but it will give you an idea.

You can get a directory list as shown here.

Pagination answered 26/10, 2011 at 7:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.