2011-09-10 57 views
1

如果你不打擾閱讀整個文本,可以跳到最後兩個點:對C#與隊列列表異步下載

這個網站已經幫助我的時候一打已經過去,但現在我真的需要幫助。

的問題如下:

  • 首先,我用show UI選項一起使用的DownloadFile功能。這很好,但用戶界面很醜,並沒有太多可用的選項。

  • 然後,我切換到DownloadFileAsync與改變的進度事件,以基本上有我自己的用戶界面。我遇到的唯一問題是我循環訪問程序必須下載的文件列表,並調用下載函數(調用DownloadAsync函數)。就像這樣:

    foreach (ListViewItem t in themeList.CheckedItems) 
         { 
          DownloadFile(file to be downloaded); 
         } 
    
  • 但顯然這並沒有工作,因爲DownloadFileAsync功能不支持在同一時間多個呼叫,因爲沒有排隊系統,如DownloadFile了,所以它會只下載第一稱爲文件。 因此,我所做的是創建一個函數,將要下載的文件添加到數組中,並通過列表使背景工作循環,然後等待調用DownloadAsync,直到完成上一個下載爲止。這有點解決了。下面是代碼:

    #region "Download functions" 
    //Function that converts download speed to a nice user friendly format 
    private static string BpsToString(double bps) 
    { 
        var m = new string[] { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; 
        var i = 0; 
        while (bps >= 0.9 * 1024) 
        { 
         bps /= 1024; 
         i++; 
        } 
    
        return String.Format("{0:0.00} {1}/sec", bps, m[i]); 
    } 
    
    private bool _complete = false; 
    private string _speed; 
    private int _secondsRemaining = -1; 
    private long _transferred = 0; 
    private Stopwatch _sw = new Stopwatch(); 
    private List<string[]> _fd = new List<string[]>(); 
    private void DownloadFile(string url, string des, bool overwrite = false) 
    { 
        if (overwrite) //if the file needs to be overwritten or not 
        { 
         if (File.Exists(des)) File.Delete(des); 
        } 
        else 
        { 
         if (File.Exists(des)) return; 
        } 
    
        if (!Directory.Exists(Path.GetDirectoryName(des))) //create the directory if it doesn't exist 
         Directory.CreateDirectory(Path.GetDirectoryName(des)); 
    
        string[] file = {url, des}; 
        _fd.Add(file); //add file to queue list 
    
        if(!backgroundDownloader.IsBusy) //if downloader isn't doing anything, start it again 
         backgroundDownloader.RunWorkerAsync(); 
    } 
    
    //function called by the backgroundworker to actually download the file 
    private void ContinueDownloadFile(string url, string des) 
    { 
        var webClient = new WebClient(); 
        webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); 
        webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged); 
        webClient.DownloadFileAsync(new Uri(_fd[0][0]), _fd[0][1]); 
    } 
    
    //when download completed, set progress bar to 0% and remove the first (0) download from the queue 
    private void Completed(object sender, AsyncCompletedEventArgs e) 
    { 
        SetProgressText("Idle"); 
        SetProgressValue(0); 
    
        if(_fd.Count != 0) 
         _fd.RemoveAt(0); 
    
        _complete = true; //if it's complete, set to true so the backgroundworker knows it can start the next download 
    } 
    
    //progress bar value change and status change for download speed etc... 
    private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) 
    { 
        if(progressLabel.Text == "Idle") 
         SetProgressText("Downloading..."); 
    
        if (_sw.Elapsed >= TimeSpan.FromSeconds(1)) 
        { 
         _sw.Stop(); 
    
         var bytes = e.BytesReceived - _transferred; 
         var bps = bytes * 1000.0/_sw.Elapsed.TotalMilliseconds; 
         _speed = BpsToString(bps); 
    
         _secondsRemaining = (int)((e.TotalBytesToReceive - e.BytesReceived)/bps); 
    
         _transferred = e.BytesReceived; 
         _sw.Reset(); 
         _sw.Start(); 
    
         SetProgressText("Downloading: " + e.ProgressPercentage + "% | Seconds remaining: " + 
         _secondsRemaining + " | Files remaining: " + _fd.Count + " | Speed: " + _speed); 
        } 
    
        SetProgressValue(e.ProgressPercentage); 
    } 
    
    //the backgroundworker who starts the downloads from the list one by one 
    private void BackgroundDownloaderDoWork(object sender, DoWorkEventArgs e) 
    { 
        while (_fd.Count != 0) 
        { 
         _sw.Start(); 
         _complete = false; //let the backgroundworker wait till the download is complete 
         ContinueDownloadFile(_fd[0][0], _fd[0][1]); 
    
         while(!_complete) //let it wait here 
          Thread.Sleep(100); 
    
         _sw.Stop(); 
         _sw.Reset(); 
        } 
    } 
    
    #endregion 
    
  • 所以basicly我的下一個問題是,程序必須等待與執行任何代碼,直至下載完成。我做這個做:

    while (_fd.Count != 0) 
         Application.DoEvents(); 
    
  • 這顯然不是最好的解決辦法,因爲他們可以通過點擊其他的事情,而下載是忙碌的,但是,是的Thread.Sleep也只是凍結一切。 相反,我會在主窗體頂部重​​點放置一個等待窗體(也許這裏是一個進度條,而不是主窗體),所以它們不能單擊主窗體並將Thread.Sleep放在主窗體上形成?

  • 你會如何解決這個問題?你是否還會使用循環遍歷文件數組的背景工作,或者是否有更簡單,更高效的方法。也許不使用DownloadFileAsync,但手動套接字下載?

  • 我basicly想要的是同步下載文件,但有自己的UI(所以我需要使用異步下載功能)。哈哈

我希望我通知你足夠。提前致謝。

+0

也許這是「TL; DR」的信息? –

+1

我會讓它更短,因爲它確實包含一些不必要的東西 – d0ggy

+0

你對子彈#5的計劃聽起來是最好的方式。您也可以嘗試使用任務並行庫(TPL)。它可以簡化併發下載調用,並且可以使用同步文件下載器。 –

回答

1

使用模式對話框,可能爲視覺滿足添加進度條,通知用戶進程正在工作。

通過這種方式,您可以在不允許與主窗體控件交互的情況下執行下載異步。

+0

謝謝,我會研究模態對話框。 – d0ggy

+0

我似乎無法關閉模態對話框以執行進一步的代碼。我試圖在等待窗體上設置一個計時器,當進度條達到100時將DialogResult設置爲OK,但它不關閉。從主窗體的背景工作人員嘗試它,但它不關閉。嗯.. – d0ggy

+0

當你開始下載時,顯示你的模式對話框。在你的異步回調中,你應該拋棄對話。 – IAbstract