Multi-part Download with Multiple Threads

This topic shows you how to use multiple threads to connect to an FTP server and download a file. First, we need to define a class which contains information about the file, and the threads. In this example, it's named GlobalInfo. And then we define a class for Ftp Threads, it's named FtpThread. Finally, in the main routine, we create and start 3 threads to download the parts of the file. As a result, the download speed is improved if the file is large. We take advantage of the GetDownloadStream method with the following example code:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Atp.Net;
using System.Threading;

namespace Samples
{
    class GlobalInfo
    {
        // Maintain some information about the threads.
        public int CompletedThreads;
        public int Threads;
        public ManualResetEvent Event;
        public byte[][] Streams;
        public long Length = -1;
        public int BlockSize;
        public bool Finish;
    }

    class FtpThread
    {
        int _offset;
        int _blockSize;
        GlobalInfo _info;
        Ftp _client;
        int _index;
        
        public FtpThread(GlobalInfo info, int index)
        {
            _info = info;
            _index = index;
        }

        public void Start()
        {
            const string remoteFile = "/pub/test.zip";
            _client = new Ftp();
            _client.Connect("atp-inc.net");
            _client.Authenticate("test", "test");

            byte[] buf;

            lock (_info)
            {
                if (_info.Length == -1)
                {
                    // Get file length.
                    _info.Length = _client.GetFileLength(remoteFile);
                    // Calculate block size.
                    _info.BlockSize = (int)(_info.Length / _info.Threads);
                    _info.Streams = new byte[_info.Threads][];
                }

                // Calculate offset.
                _offset = _info.BlockSize * _index;
                _blockSize = (int)((_index == _info.Threads - 1) ? (_info.Length - _index * _info.BlockSize) : _info.BlockSize);

                buf = new byte[_blockSize];
                _info.Streams[_index] = buf;            
            }

            // Get download stream.
            Stream downloader = _client.GetDownloadStream(remoteFile, SeekOrigin.Begin, _offset);
            int count = 0;
            // Download data.
            while (true)
            {
                int size = (count + 4048 > _blockSize) ? (_blockSize - count) : 4048;
                int read = downloader.Read(buf, count, size);
                if (count + read >= _blockSize || read == 0)
                    break;
                count += read;
            } 
            downloader.Close();            

            _client.Disconnect();

            lock (_info)
            {
                _info.CompletedThreads++;
                if (_info.CompletedThreads == _info.Threads && !_info.Finish)
                {
                    _info.Event.Set();
                    // All threads have completed.

                    // Combine
                    using (FileStream sw = System.IO.File.OpenWrite(@"c:\temp\test.zip"))
                    {
                        foreach (byte[] s in _info.Streams)
                        {
                            sw.Write(s, 0, (int)s.Length);
                        }
                    }

                    _info.Finish = true;
                }
            }
        }
    }

    class MultiParts
    {
        static ManualResetEvent _event = new ManualResetEvent(false);
        static Ftp client;

        static void Main()
        {
            const int threads = 3;

            // Use 3 threads to download a remote file to streams and then save the streams into a local file.
            int offset = 0;
            
            GlobalInfo ginfo = new GlobalInfo();
            ginfo.Threads = threads;
            ginfo.Event = _event;

            for (int i = 0; i < threads; i++)
            {
                FtpThread fthread = new FtpThread(ginfo, i);
                Thread th = new Thread(fthread.Start);
                th.Start();
            }
            _event.WaitOne();
        }
    }
}

Click here to download the Ultimate FTP Component for .NET, or here to download the .NET CF version.