There is an FTP server from which to download files, for this I use WebClient. And there is also the likelihood of disconnection at boot time.

This is the code that is currently downloading the file.

private static BlockingCollection<FileItem> downloadQueue; private static void StartDownloadTask() { FileItem file; if (downloadQueue.Count > 0 && downloadQueue.TryTake(out file)) { using (WebClient client = InitWebClient()) { Task t; t = client.DownloadFileTaskAsync(file.RemoteUri, file.SavePath); t.ContinueWith(OnTaskComplete); } } } private static WebClient InitWebClient() { WebClient client = new WebClient(); client.Credentials = new NetworkCredential(Settings.Login, Settings.Password); return client; } 

I need to pause the file download, if the connection is gone, and wait for a while until the connection is restored. And then continue downloading. How can this be implemented?

  • If lost, it is unlikely to recover. It may even happen that recovery will wait forever. The download stops on timeout, the connection is broken. A new one is installed and the download continues from the interrupted place. After all, the file failed download you do not delete? This is the only way to know where the download was interrupted. - Sergey
  • Got it. In this case, you will need to intercept WebException and put the failed file back into the download queue. - Simple Ned

1 answer 1

No, no, you'll upload the file all over again, even if you didn’t reload a couple of bytes. Save in the task for loading a list of underloaded areas, and load only the necessary one.

Well, it is better to use async / await (the code of a suitable producer / consumer is taken from here ).

Here is the outline of the code:

 async Task SingleConsumerTask() { while (await downloadQueue.OutputAvailableAsync()) { FileItem file; if (!downloadQueue.TryReceive(out file)) continue; try { if (file.Range.End == null) { var sizeRequest = (FtpWebRequest)InitWebRequest(file.RemoteUri); sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize; using (var response = (FtpWebResponse)await sizeRequest.GetResponseAsync()) file.Range.End = response.ContentLength; } var request = (FtpWebRequest)InitWebRequest(file.RemoteUri); request.Method = WebRequestMethods.Ftp.DownloadFile; request.ContentOffset = file.Range.Start; using (var response = (FtpWebResponse)await request.GetResponseAsync()) { var buf = new byte[1024 * 1024]; var s = response.GetResponseStream(); while (file.Range.Start < file.Range.End) { var actuallyRead = await s.ReadAsync(buf, 0, buf.Length); if (actuallyRead == 0) // error { file.numberOfRetries++; break; } file.Range.Start += actuallyRead; // у вас есть буфер с данными, отправьте его в очередь на запись в файл // и загружайте тем временем дальше } } } catch (WebException) { file.numberOfRetries++; } finally { if (file.Range.End == null || file.Range.Start < file.Range.End) { if (file.numberOfRetries < 10) { await downloadQueue.SendAsync(file); } else { // запостить в список зафейлившихся загрузок } } } } } private static WebRequest InitWebRequest(string uri) { var request = WebRequest.Create(uri); request.Credentials = new NetworkCredential(Settings.Login, Settings.Password); return request; } 

Well, if you want to pause before reconnecting, add to FileInfo time from which you can try to load again.

  • @SimpleNed: Well, yes, but your code seems to load the file via DownloadFileTaskAsync every time from the beginning, right? - VladD
  • No, the file remains. But I checked that if I run the task via 'DownloadFileTaskAsync', then if the connection is lost, the file will remain on the disk. And if you create an empty task, and use 'DownloadFile' inside it, then the file is deleted when it is broken. I decided not to play dances with a tambourine and refused to use WebClient in favor of FtpWebRequest, since to read a thread from the beginning, one has to use 'request.ContentOffset'. It is probably also present in the WebClient, but it is not recommended to use it for these purposes on MSDN - Simple Ned
  • @SimpleNed: File remains, yes. But DownloadFileTaskAsync loads it first, right? In my example, by the way, just FtpWebRequest . - VladD
  • Loads first, you are right, yes. At first I wanted to get a stream from WebClient , but for the above reasons I have already returned to your version - Simple Ned