【问题标题】:Download multiple files simultaneously from FTP using FluentFTP使用 FluentFTP 从 FTP 同时下载多个文件
【发布时间】:2020-07-04 22:17:19
【问题描述】:

我正在使用 FluentFTP 从 FTP 进行连接、下载等操作
https://github.com/robinrodricks/FluentFTP/wiki

我想同时从 List 下载文件。一个个下载没问题。

这就是我的代码的样子:

下载功能:

public async Task<bool> DownloadFileAsync(string RemoteUrl, string AppName, Progress<FtpProgress> progress = null)
    {
        return await Task.Run(async() =>
        {

            using (FileStream read = new FileStream("settings.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                if (ftpClient.IsConnected)
                {
                    if (File.Exists("settings.xml"))
                    {
                        Information info = (Information)xs.Deserialize(read);
                    
                        if (Directory.Exists(info.Downloads))
                        {
                            bool DownloadFinished = await ftpClient.DownloadFileAsync(info.Downloads + "\\" + AppName, RemoteUrl, FtpLocalExists.Overwrite, FtpVerify.Retry, progress);
                            if (DownloadFinished == true)
                            {
                                loger.LogWrite("File " + RemoteUrl + " downloaded succesfully.");
                                //read.Dispose();
                                return true;
                            }
                            else
                            {
                                loger.LogWrite("File" + RemoteUrl + " download failed.");
                                //read.Dispose();
                                return false;
                            }
                        }
                        else
                        {
                            loger.LogWrite("Could not locate folder " + info.Downloads + " downloading terminated.");
                            return false;
                        }
                    }
                    else
                    {
                        MessageBox.Show("settings.xml file is missing.");
                        loger.LogWrite("settings.xml file is missing.");
                        read.Dispose();
                        return false;
                    }
                }
                else
                {
                    loger.LogWrite("FTP Client is not connected could not download: " + RemoteUrl);
                    read.Dispose();
                    return false;
                }
            }
        });

    }

我如何填写列表:

Arta_Variables.ArtaSoftware.Add(new Software() { RemoteUrl = "Ultra_Script/Basic_SW/Adobe_Reader.exe", SoftwareName = "Adobe_Reader.exe", FileExistsOnRemoteUrl = null, Downloaded = null });

这是一个一个下载它们的方法:

if(Arta_Variables.DAAOChecked == false)
{
    if (CheckFinished == true)
    {
        using (FileStream read = new FileStream("settings.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
        {

            XmlSerializer xs = new XmlSerializer(typeof(Information));
            Information info = (Information)xs.Deserialize(read);

            AddBlackLine("");
            AddBlackLine("Downloading all available files.");
            AddBlackLine("");

            foreach (Software software1 in ArtaChosenSW)
            {

                string item = software1.SoftwareName;
                int index = ArtaChosenSW.FindIndex(p => p.SoftwareName == item);

                if (software1.FileExistsOnRemoteUrl == true)
                {
                    AddBlackLine("Downloading " + software1.SoftwareName);

                    Dispatcher.Invoke(() =>
                    {
                        DWGProgressLab.Visibility = Visibility.Visible;
                        DP_ProgressPercentage.Visibility = Visibility.Visible;
                    });

                    Progress<FtpProgress> prog = new Progress<FtpProgress>(x =>
                    {
                        int ConvertedInt = (int)x.Progress;
                        DP_ProgressPercentage.Dispatcher.BeginInvoke((Action)(() => DP_ProgressPercentage.Content = ConvertedInt + "%"));
                    });

                    bool DWFinished = await ftp.DownloadFileAsync(software1.RemoteUrl, software1.SoftwareName, prog);

                    if (DWFinished == true)
                    {
                        AddGreenLine("Download of " + software1.SoftwareName + " succesfull.");
                        ArtaChosenSW[index].Downloaded = true;
                        ArtaChosenSW[index].LocalUrl = info.Downloads;

                        Dispatcher.Invoke(() =>
                        {
                            DWGProgressLab.Visibility = Visibility.Hidden;
                            DP_ProgressPercentage.Visibility = Visibility.Hidden;
                        });

                    }
                    else
                    {
                        AddRedLine("Download of " + software1.SoftwareName + " failed");
                        ArtaChosenSW[index].FileExistsOnRemoteUrl = false;
                    }
                }
                else
                {
                    ArtaChosenSW[index].FileExistsOnRemoteUrl = true;
                    AddBlackLine("File " + software1.SoftwareName + " did not found on ftp. Could not download.");
                    loger.LogWrite("File " + software1.SoftwareName + " did not found on ftp. Could not download.");
                }
            }
        }
    }
}

我尝试同时下载:

foreach(Software software in ArtaChosenSW)
{
    var tasks = ArtaChosenSW.Select(c => Task.Factory.StartNew(() => ftp.DownloadFileAsync(c.RemoteUrl, c.SoftwareName))).ToArray();
    Task.WaitAll(tasks);
}

可悲的是,它在本地 url 中创建了 0kb 的空白文件,但没有进行下载。

我在异步编程方面没有太多经验,所以我会很高兴得到所有答案或一些更好的方法。

【问题讨论】:

    标签: c# .net asynchronous ftp fluentftp


    【解决方案1】:

    您的所有传输似乎都在使用一个FtpClient 实例。

    FtpClient 代表一个到 FTP 服务器的连接。 FTP 协议不允许通过一个连接进行多个并行传输。您必须为每次并行传输打开一个新连接。

    有关实现示例,请参阅Download multiple files concurrently from FTP using FluentFTP with a maximum value

    【讨论】:

      【解决方案2】:

      从您发布的答案中获取您的代码,这应该可以处理多个下载,而无需任何线程池或 Task 包装器开销。

      public async Task DownloadMultipleFilesConcurrentFromFTP(int NumberOfConnections, string AppName, string RemoteUrl)
      {
          var downloadTasks = new List<Task>();
          for (int i = 0; i < NumberOfConnections; i++){
              downloadTasks.Add(DownloadFile(AppName, RemoteUrl));
          }
          await Task.WhenAll(downloadTasks);
      }
       
      public async Task DownloadFile(string AppName, string RemoteUrl)
      {
          var ftp = new FtpClient(FTPHost, networkCredential);
          ftp.ConnectTimeout = ConnectTimeout;
          ftp.SocketPollInterval = SocketPollInterval;
          ftp.ReadTimeout = ReadTimeout;
          ftp.DataConnectionConnectTimeout = DataConnectionConnectTimeout;
          ftp.DataConnectionReadTimeout = DataConnectionReadTimeout;
          await ftp.ConnectAsync();
          if (ftp.IsConnected)
          {
              using (FileStream read = new FileStream("settings.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
              {
                  if (ftp.IsConnected == true)
                  {
                      if (File.Exists("settings.xml"))
                      {
                          Information info = (Information)xs.Deserialize(read);
                          if (Directory.Exists(info.Downloads))
                          {
                              await ftp.DownloadFileAsync(info.Downloads + "\\" + AppName, RemoteUrl, FtpLocalExists.Overwrite, FtpVerify.Retry);
                          }
                      }
                  }
              }
          }
      }
      

      它为每次下载创建一个Task,然后在任务列表上调用Task.WhenAll,等待所有文件完成后再返回。

      我没有修改您的任何文件处理代码,但您也应该考虑使用async 版本的调用,因为使用阻塞调用访问文件系统可能会导致响应问题。

      【讨论】:

        【解决方案3】:

        根据@Martin Prikryl 的回答,我为并发下载创建了新功能:

        完美运行:

        public async Task DownloadMultipleFilesConcurrentFromFTP(int NumberOfConnections, string AppName, string RemoteUrl)
        {
            await Task.Run(async() =>
            {
                NetworkCredential networkCredential = new NetworkCredential(FTPUsername, FTPPassword);
                List<FtpClient> ftpClients = new List<FtpClient>();
        
                for (int i = 0; i < NumberOfConnections; i++)
                {
                    ftpClients.Add(new FtpClient(FTPHost, networkCredential));
                    foreach(FtpClient ftp in ftpClients)
                    {
                        ftp.ConnectTimeout = ConnectTimeout;
                        ftp.SocketPollInterval = SocketPollInterval;
                        ftp.ReadTimeout = ReadTimeout;
                        ftp.DataConnectionConnectTimeout = DataConnectionConnectTimeout;
                        ftp.DataConnectionReadTimeout = DataConnectionReadTimeout;
                        ftp.Connect();
                        if (ftp.IsConnected)
                        {
                            using (FileStream read = new FileStream("settings.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                            {
                                if(ftp.IsConnected == true)
                                {
                                    if (File.Exists("settings.xml"))
                                    {
                                        Information info = (Information)xs.Deserialize(read);
                                        if (Directory.Exists(info.Downloads))
                                        {
                                            await ftp.DownloadFileAsync(info.Downloads + "\\" + AppName, RemoteUrl, FtpLocalExists.Overwrite, FtpVerify.Retry);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
        

        现在我只需要弄清楚如何返回所有文件的下载进度。

        【讨论】:

        • 如果您不使用Task.Run,这会更好,而是为每个下载作业创建一个普通的Task,并使用Task.WhenAll 等待它们。这将允许您直接从下载 Task 中报告进度,并有助于避免多线程问题。
        • 你也应该等待ftp.ConnectAsync(),而不是调用ftp.Connect()
        • @BradleyUffner 我认为这一行是为每个 ftpclient 创建任务: var tasks = ArtaChosenSW.Select(c => Task.Factory.StartNew(() => ftp.DownloadMultipleFilesConcurrentFromFTP(ArtaChosenSW.Count, c .RemoteUrl, c.SoftwareName))).ToArray();
        • 有点。它围绕匿名delegate 的调用创建一个Task,当线程退出时完成。基本上,它是在线程池中的另一个线程上运行的代码的包装器,这在与 UI 交互时增加了一些低效率和复杂性。如果您要在不使用Task.Run 的情况下执行此操作,则可以完全不涉及多线程。
        • 重新审视这个答案,我认为这不再是一个好答案。首先是for (int i = 0; i &lt; NumberOfConnections; i++)foreach(FtpClient ftp in ftpClients) 的嵌套。您最终得到 1+2+3+4+...+N 个连接,而不是 N 个连接。 – 但无论如何,您一次都不会使用超过一个连接。该代码根本没有做它声称的事情。 + 您一直在下载相同的文件。 + 其他不太重要的问题,例如检查您已经打开的文件是否存在等。
        猜你喜欢
        • 2021-05-20
        • 2017-05-18
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多