Search code examples
c#.netmultithreadingftpftpwebrequest

Download single file from FTP server in chunks in multiple threads using C#


I just wanted to download a single file from FTP server in multiple segments in multiple threads using C#.

Is it possible to give ranges for the file download like in HttpWebRequest?


Solution

  • You can use FtpWebRequest.ContentOffset to specify the starting offset.

    But FtpWebRequest.ContentLength is not implemented. To workaround that you have to abort the download, once you receive the desired amount of bytes.

    const string name = "bigfile.dat";
    const int chunks = 3;
    const string url = "ftp://example.com/remote/path/" + name;
    NetworkCredential credentials = new NetworkCredential("username", "password");
    
    Console.WriteLine("Starting...");
    
    FtpWebRequest sizeRequest = (FtpWebRequest)WebRequest.Create(url);
    sizeRequest.Credentials = credentials;
    sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize;
    long size = sizeRequest.GetResponse().ContentLength;
    Console.WriteLine($"File has {size} bytes");
    long chunkLength = size / chunks;
    
    List<Task> tasks = new List<Task>();
    
    for (int chunk = 0; chunk < chunks; chunk++)
    {
        int i = chunk;
        tasks.Add(Task.Run(() =>
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
                request.Credentials = credentials;
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.ContentOffset = chunkLength * i;
                long toread =
                    (i < chunks - 1) ? chunkLength : size - request.ContentOffset;
                Console.WriteLine(
                    $"Downloading chunk {i + 1}/{chunks} with {toread} bytes ...");
    
                using (Stream ftpStream = request.GetResponse().GetResponseStream())
                using (Stream fileStream = File.Create(name + "." + i))
                {
                    byte[] buffer = new byte[10240];
                    int read;
                    while (((read = (int)Math.Min(buffer.Length, toread)) > 0) &&
                           ((read = ftpStream.Read(buffer, 0, read)) > 0))
                    {
                        fileStream.Write(buffer, 0, read);
                        toread -= read;
                    }
                }
                Console.WriteLine($"Downloaded chunk {i + 1}/{chunks}");
            }));
    }
    
    Console.WriteLine(
        "Started all chunks downloads, waiting for them to complete...");
    Task.WaitAll(tasks.ToArray());
    
    Console.WriteLine("Done");
    Console.ReadKey();