Загрузка одного файла с FTP-сервера в виде фрагментов в несколько потоков с использованием C # - PullRequest
1 голос
/ 05 ноября 2019

Я просто хотел загрузить один файл с FTP-сервера в несколько сегментов в нескольких потоках, используя C #.

Можно ли указать диапазоны для загрузки файла, как в HttpWebRequest?

Ответы [ 2 ]

1 голос
/ 05 ноября 2019

Во-первых, отказ от ответственности: многозадачность - это , а не волшебная пуля "иди быстрее". Если вы примените его к неправильной проблеме, вы получите код, который более сложен / подвержен ошибкам, требует больше памяти и на самом деле медленнее , чем обычный старый синглетный / последовательный подход. Одна альтернативная задача для длительной операции обычно обязательна. Но массовое распараллеливание только в очень определенных обстоятельствах.

Общие файловые операции связаны с диском или сетью. Многозадачность не увеличит скорость операций с диском или сетью. И действительно, это может привести к замедлению, так как NCQ и аналогичные функции должны выправлять ваши запросы произвольного доступа. Это сказанное с Netowrking это иногда может помочь. Некоторые серверы применяют лимит «на соединение», и, таким образом, разделение загрузки на несколько сегментов с собственным соединением может привести к ускорению сети. Но будьте уверены, что это действительно так. Рассмотрим все, кроме пункта 1 Speed ​​Rant .

Предполагается, что FTPWebRequest по-прежнему используется классом, который выглядит как ContentLenght и ContentOffset может быть дроидами, которых вы ищете. Вы в основном используете это подобно подстроке - каждое соединение / подзапрос берет X байтов из Y смещения.

0 голосов
/ 06 ноября 2019

Вы можете использовать FtpWebRequest.ContentOffset для указания начального смещения.

Но FtpWebRequest.ContentLength не реализовано. Чтобы обойти это, необходимо прервать загрузку, как только вы получите желаемое количество байтов.

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