C# Dateidownload mit Fortschrittsbalken

eightcore

Lt. Commander
🎅Rätsel-Elite ’24
Registriert
Juli 2008
Beiträge
1.676
Guten Morgen. Ich schreibe ein Programm, das unter anderem die aktuellsten Versionen der Hilfsprogramme (7-Zip usw.) herunterlädt.

Nun, dazu gehört auch der Adobe Reader und dessen Setup-Datei wiegt 35 MB.
Ich möchte nun den Endbenutzer mit Hilfe einer Progress-Bar über den Downloadsfortschritt informieren.

Realisiert habe ich den Download mit WebClient.DownloadFile.

Wie kann ich Den Download-Fortschritt durch die ProgressBar anzeigen lassen?




MfG | eightcore
 
Tjo, du musst das asynchron machen, also mit WebClient.DownloadFileAsync(...) und dann die Events der WebClient-Klasse verarbeiten, also WebClient.DownloadProgressChanged um deine ProgressBar zu aktualisieren und WebClient.DownloadFileCompleted, wenn's fertig ist... innerhalb der Funktionen, denen du das Event zugewiesen hast wirst du aber Callbacks brauchen um auf deine ProgressBar-Control zugreifen zu können, weil die Events aus einem anderen Thread kommen.

Nachtrag: asynchron muss das deshalb passieren, weil die synchrone Funktion dein ganzes Programm blockiert bis die Operation abgeschlossen ist - währenddessen herrscht Stillstand. Beim asynchronen Aufruf arbeitet dein Programm weiter, d.h. du musst aufpassen, dass du dann nicht 10 Sachen gleichzeitig runterlädst - im Gegenzug ist man dann aber in der Lage andere Sachen wie z.B. das Aktualisieren einer ProgressBar durchzuführen ;)
 
Zuletzt bearbeitet: (Ergänzung)
Ob es mit WebClient geht, weiß ich nicht genau. Ich verwende immer WebRequest und WebResponse.
Da kann man die Header der Datenströme abgreifen und so auch die Menge der Daten erfahren die runterzuladen sind.
Dann setzt man die Progressbar auf 0 Min und 100 Max und die Value auf den Prozentwert von Aktueller Position im Stream * 100 / Länge des Streams.
Da der Download in einer Schleife erfolgt erübrigt sich auch die Frage wo man die Progressbar aktualisieren kann ;)

Dann erspart man sich auch asynchrones Arbeiten und die damit notwendigen Invoke-Konstrukte.
 
Jo gut, auch ne Möglichkeit ;) Ich selbst bin ein Fan von asynchronen Herangehensweisen, weil man da noch ne ganze Ecke mehr Freiheiten hat und vollständig event-basierte Quelltexte einfach schön zu lesen sind bzw. sein können, aber deine Herangehensweise geht natürlich auch. :D
 
Asynchron muss er es nicht machen, er kann die Downloadgeschichten auch einfach in einen eigenen Thread/BackgroundWorker packen. Funktioniert genauso und die GUI bleibt nicht hängen. Vielleicht mal ein Teil aus meinem kleinen Download-Programm:
PHP:
public partial class Form1 : Form
{
  private Thread DownloadThread;

  public Form1()
  {
    InitializeComponent();

    Directory.CreateDirectory( Path.GetDirectoryName( Application.ExecutablePath ) + "\\maps\\" );
  }

  private void toolStripButton1_Click( object sender, EventArgs e )
  {
    listBox1.Items.Clear();

    int start, end;
    Int32.TryParse( ToolStripTextBoxFromID.Text, out start );
    Int32.TryParse( ToolStripTextBoxEndID.Text, out end );

    ToolStripProgressBar.Minimum = start;
    ToolStripProgressBar.Maximum = end;
    ToolStripProgressBar.Value = start;

    DownloadThread = new Thread( new ThreadStart( DownloadMaps ) );
    DownloadThread.Start();
  }

  public string NewItem
  {
    set
    {
      this.Invoke( (MethodInvoker)delegate
      {
        this.listBox1.Items.Add( value );
        this.listBox1.SelectedIndex = this.listBox1.Items.Count - 1;
        this.listBox1.SelectedIndex = -1;
      } );
    }
  }

  public int Progress
  {
    set
    {
      this.Invoke( (MethodInvoker)delegate { this.ToolStripProgressBar.Value = value; } );
      this.Invoke( (MethodInvoker)delegate { this.ToolStripLabelCurrentID.Text = value.ToString(); } );
    }
  }

  public void DownloadMaps()
  {
    Int64 StartID, EndID;

    Int64.TryParse( ToolStripTextBoxFromID.Text, out StartID );
    Int64.TryParse( ToolStripTextBoxEndID.Text, out EndID );

    string url = "...";
    HttpWebRequest req = null;
    HttpWebResponse res = null;
    string dir = Path.GetDirectoryName( Application.ExecutablePath ) + "\\maps\\";
    string filename = "";
    Stream str = null;
    byte[] buf = null;
    FileStream fstr = null;
    int id;

    for( Int64 i = StartID; i <= EndID; i++ )
    {
      req = (HttpWebRequest)WebRequest.Create( url + i.ToString() );
      res = (HttpWebResponse)req.GetResponse();
      filename = "";

      NewItem = "id = " + i.ToString() + " = " + res.StatusCode.ToString();
      int.TryParse( i.ToString(), out id );
      Progress = id;

      if( res.StatusCode != HttpStatusCode.OK )
        continue;

      foreach( string r in res.Headers )
      {
        string s = res.Headers[r];
        if( s.IndexOf( "filename" ) != -1 )
        {
          int start = s.IndexOf( "filename" ) + ("filename").Length + 2,
            end = s.Length - start - 1;
          filename = s.Substring( start, end );
          break;
        }
      }

      NewItem = "completed analyzing headers...";
      if( filename == string.Empty )
      {
        NewItem = "invalid filename... skipping file...";
        req.Abort();
        res.Close();
        continue;
      }
      NewItem = "filename = \"" + filename + "\"";

      NewItem = "write stream to file \"" + dir + filename + "\"";

      str = res.GetResponseStream();
      buf = new byte[1024];
      fstr = new FileStream( dir + filename, FileMode.Create );
      StreamExtensions.CopyTo( str, fstr );

      str.Close();
      fstr.Close();

      req.Abort();
      res.Close();

      NewItem = "completed write to file";
    }
  }

  private void toolStripButton2_Click( object sender, EventArgs e )
  {
    DownloadThread.Abort();
  }
}

public static class StreamExtensions
{
  public static void CopyTo( this System.IO.Stream src, System.IO.Stream dest )
  {
    if( src == null )
      throw new System.ArgumentNullException( "src" );
    if( dest == null )
      throw new System.ArgumentNullException( "dest" );

    System.Diagnostics.Debug.Assert( src.CanRead, "src.CanRead" );
    System.Diagnostics.Debug.Assert( dest.CanWrite, "dest.CanWrite" );

    int readCount;
    var buffer = new byte[8192];
    while( (readCount = src.Read( buffer, 0, buffer.Length )) != 0 )
      dest.Write( buffer, 0, readCount );
  }
}
 
@Yuuri: Und ein extra Thread den man manuell anhalten und starten muss, ist besser als es asynchron zu machen wo sich der Threadpool um das ganze Threading kümmert und man zusätzlich sogar noch Events geschenkt bekommt?
 
Wieso manuell anhalten und starten? Kommt doch drauf an, wie du ihn designst. Der Thread kann sich doch komplett selbst kümmern, indem er einfach Parameter/URLs übergeben bekommt und diese nacheinander abarbeitet. Das Einzige was doch getan werden muss, ist die GUI zu aktualisieren und das geht doch einfachst durch einen Invoke des jeweiligen Controls.
 
Ja die Frage ist doch eher, warum sollte ich nen Extra Thread manuell Starten(was du ja über thread.Start() machst) wenn ich das alles Kostenlos vom ThreadPool geschenkt bekomme und nur noch die Events ausimplementieren muss? Ich versteh grad nicht den Vorteil an der Sache gegenüber der Asynchronen Methoden. Wenn du mich vielleicht mehr aufklärst dann lass ich mich auch gerne von deiner Methode überzeugen :)
 
Ich hab es ja auch nicht als Vorteil angepriesen, sondern nur als Alternative. Kommt halt drauf an wie komplex er die Sache gestaltet. Ab Punkt x ist es sicherlich von Vorteil Variante a statt b zu nehmen. Soll er halt beides ausprobieren und sehen, mit was er besser zurecht kommt.
 
Zurück
Oben