Cómo: Descargar un archivo en segundo plano

Actualización: noviembre 2007

Descargar archivos es una tarea común y suele resultar de utilidad ejecutar esta operación que potencialmente invierte mucho tiempo en un subproceso independiente. Utilice el componente BackgroundWorker para lograr esta tarea con muy poco código.

Ejemplo

En el ejemplo de código siguiente se muestra cómo utilizar un componente BackgroundWorker para cargar un archivo XML de una dirección URL. Cuando el usuario hace clic en el botón Descargar, el controlador de eventos Click llama al método RunWorkerAsync de un componente BackgroundWorker para iniciar la operación de descarga. El botón se deshabilita por la duración de la descarga y, a continuación, se habilita cuando la descarga está completa. MessageBox muestra el contenido del archivo.

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms
Imports System.Xml

Public Class Form1
   Inherits Form
   Private WithEvents backgroundWorker1 As BackgroundWorker
   Private WithEvents dowloadButton As Button
   Private document As XmlDocument = Nothing


   Public Sub New()
      InitializeComponent()
    End Sub

    Private Sub dowloadButton_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) _
    Handles dowloadButton.Click

        ' Start the download operation in the background.
        Me.backgroundWorker1.RunWorkerAsync()

        ' Disable the button for the duration of the download.
        Me.dowloadButton.Enabled = False

        ' Wait for the BackgroundWorker to finish the download.
        While Me.backgroundWorker1.IsBusy
            ' Keep UI messages moving, so the form remains 
            ' responsive during the asynchronous operation.
            Application.DoEvents()
        End While

        ' The download is done, so enable the button.
        Me.dowloadButton.Enabled = True
    End Sub

    Private Sub backgroundWorker1_DoWork( _
    ByVal sender As Object, _
    ByVal e As DoWorkEventArgs) _
    Handles backgroundWorker1.DoWork

        document = New XmlDocument()

        ' Replace this file name with a valid file name.
        document.Load("http://www.tailspintoys.com/sample.xml")

        ' Uncomment the following line to
        ' simulate a noticeable latency.
        'Thread.Sleep(5000);
    End Sub

   Private Sub backgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted
      If e.Error Is Nothing Then
         MessageBox.Show(document.InnerXml, "Download Complete")
      Else
         MessageBox.Show("Failed to download file", "Download failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End If
    End Sub

    ' <summary>
    ' Required designer variable.
    ' </summary>
   Private components As System.ComponentModel.IContainer = Nothing

    ' <summary>
    ' Clean up any resources being used.
    ' </summary>
    ' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso (components IsNot Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub

   #Region "Windows Form Designer generated code"


    ' <summary>
    ' Required method for Designer support - do not modify
    ' the contents of this method with the code editor.
    ' </summary>
   Private Sub InitializeComponent()
      Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker()
      Me.dowloadButton = New System.Windows.Forms.Button()
      Me.SuspendLayout()
      ' 
      ' backgroundWorker1
      ' 
      ' 
      ' dowloadButton
      ' 
      Me.dowloadButton.Location = New System.Drawing.Point(12, 12)
      Me.dowloadButton.Name = "dowloadButton"
      Me.dowloadButton.Size = New System.Drawing.Size(75, 23)
      Me.dowloadButton.TabIndex = 0
      Me.dowloadButton.Text = "Download file"
      Me.dowloadButton.UseVisualStyleBackColor = True
      ' 
      ' Form1
      ' 
      Me.AutoScaleDimensions = New System.Drawing.SizeF(6F, 13F)
      Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
      Me.ClientSize = New System.Drawing.Size(104, 54)
      Me.Controls.Add(dowloadButton)
      Me.Name = "Form1"
      Me.Text = "Form1"
      Me.ResumeLayout(False)
    End Sub

   #End Region
End Class


Public Class Program

    ' <summary>
    ' The main entry point for the application.
    ' </summary>
    <STAThread()> _
    Shared Sub Main()
        Application.EnableVisualStyles()
        Application.Run(New Form1())
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Xml;

public class Form1 : Form
{
    private BackgroundWorker backgroundWorker1;
    private Button dowloadButton;
    private XmlDocument document = null;

    public Form1()
    {
        InitializeComponent();
    }

    private void dowloadButton_Click(object sender, EventArgs e)
    {
        // Start the download operation in the background.
        this.backgroundWorker1.RunWorkerAsync();

        // Disable the button for the duration of the download.
        this.dowloadButton.Enabled = false;

        // Wait for the BackgroundWorker to finish the download.
        while (this.backgroundWorker1.IsBusy)
        {
            // Keep UI messages moving, so the form remains 
            // responsive during the asynchronous operation.
            Application.DoEvents();
        }

        // The download is done, so enable the button.
        this.dowloadButton.Enabled = true;
    }

    private void backgroundWorker1_DoWork(
        object sender, 
        DoWorkEventArgs e)
    {
        document = new XmlDocument();

        // Replace this file name with a valid file name.
        document.Load(@"http://www.tailspintoys.com/sample.xml");

        // Uncomment the following line to
        // simulate a noticeable latency.
        //Thread.Sleep(5000);
    }

    private void backgroundWorker1_RunWorkerCompleted(
        object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            MessageBox.Show(document.InnerXml, "Download Complete");
        }
        else
        {
            MessageBox.Show(
                "Failed to download file", 
                "Download failed", 
                MessageBoxButtons.OK, 
                MessageBoxIcon.Error );
        }
    }

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
        this.dowloadButton = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // backgroundWorker1
        // 
        this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
        this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
        // 
        // dowloadButton
        // 
        this.dowloadButton.Location = new System.Drawing.Point(12, 12);
        this.dowloadButton.Name = "dowloadButton";
        this.dowloadButton.Size = new System.Drawing.Size(75, 23);
        this.dowloadButton.TabIndex = 0;
        this.dowloadButton.Text = "Download file";
        this.dowloadButton.UseVisualStyleBackColor = true;
        this.dowloadButton.Click += new System.EventHandler(this.dowloadButton_Click);
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(104, 54);
        this.Controls.Add(this.dowloadButton);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

    }

    #endregion
}

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Form1());
    }
}

Descargar el archivo

El archivo se descarga en el subproceso de trabajo del componente BackgroundWorker, que ejecuta el controlador de eventos DoWork. Este subproceso se inicia cuando el código llama al método RunWorkerAsync.

Private Sub backgroundWorker1_DoWork( _
ByVal sender As Object, _
ByVal e As DoWorkEventArgs) _
Handles backgroundWorker1.DoWork

    document = New XmlDocument()

    ' Replace this file name with a valid file name.
    document.Load("http://www.tailspintoys.com/sample.xml")

    ' Uncomment the following line to
    ' simulate a noticeable latency.
    'Thread.Sleep(5000);
End Sub
private void backgroundWorker1_DoWork(
    object sender, 
    DoWorkEventArgs e)
{
    document = new XmlDocument();

    // Replace this file name with a valid file name.
    document.Load(@"http://www.tailspintoys.com/sample.xml");

    // Uncomment the following line to
    // simulate a noticeable latency.
    //Thread.Sleep(5000);
}

Esperar la finalización de un BackgroundWorker

El controlador de eventos dowloadButton_Click muestra cómo esperar a que un componente BackgroundWorker finalice su tarea asincrónica. Utilice la propiedad IsBusy para determinar si el subproceso BackgroundWorker todavía está en ejecución. Si el código se encuentra en el subproceso de la IU principal, como le pasa al controlador de eventos Click, asegúrese de llamar al método Application.DoEvents para mantener la interfaz de usuario (IU) con capacidad de respuesta.

Private Sub dowloadButton_Click( _
ByVal sender As Object, _
ByVal e As EventArgs) _
Handles dowloadButton.Click

    ' Start the download operation in the background.
    Me.backgroundWorker1.RunWorkerAsync()

    ' Disable the button for the duration of the download.
    Me.dowloadButton.Enabled = False

    ' Wait for the BackgroundWorker to finish the download.
    While Me.backgroundWorker1.IsBusy
        ' Keep UI messages moving, so the form remains 
        ' responsive during the asynchronous operation.
        Application.DoEvents()
    End While

    ' The download is done, so enable the button.
    Me.dowloadButton.Enabled = True
End Sub
private void dowloadButton_Click(object sender, EventArgs e)
{
    // Start the download operation in the background.
    this.backgroundWorker1.RunWorkerAsync();

    // Disable the button for the duration of the download.
    this.dowloadButton.Enabled = false;

    // Wait for the BackgroundWorker to finish the download.
    while (this.backgroundWorker1.IsBusy)
    {
        // Keep UI messages moving, so the form remains 
        // responsive during the asynchronous operation.
        Application.DoEvents();
    }

    // The download is done, so enable the button.
    this.dowloadButton.Enabled = true;
}

Mostrar el resultado

El método backgroundWorker1_RunWorkerCompleted controla el evento RunWorkerCompleted y se le llama cuando finaliza la operación en segundo plano. Comprueba primero la propiedad AsyncCompletedEventArgs.Error y, si ésta es null, muestra el contenido del archivo.

Private Sub backgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted
   If e.Error Is Nothing Then
      MessageBox.Show(document.InnerXml, "Download Complete")
   Else
      MessageBox.Show("Failed to download file", "Download failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
   End If
 End Sub
private void backgroundWorker1_RunWorkerCompleted(
    object sender, 
    RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
        MessageBox.Show(document.InnerXml, "Download Complete");
    }
    else
    {
        MessageBox.Show(
            "Failed to download file", 
            "Download failed", 
            MessageBoxButtons.OK, 
            MessageBoxIcon.Error );
    }
}

Compilar el código

Este ejemplo requiere:

  • Referencias a los ensamblados System.Drawing, System.Windows.Forms y System.Xml.

Para obtener información acerca de la generación de este ejemplo desde la línea de comandos de Visual Basic o Visual C#, vea Generar desde la línea de comandos (Visual Basic) o Generar la línea de comandos con csc.exe. También puede generar este ejemplo en Visual Studio pegando el código en un proyecto nuevo.

Programación eficaz

Compruebe siempre la propiedad AsyncCompletedEventArgs.Error en el controlador de eventos RunWorkerCompleted antes de intentar obtener acceso a la propiedad RunWorkerCompletedEventArgs.Result o a cualquier otro objeto que se vea afectado por el controlador de eventos DoWork.

Vea también

Tareas

Cómo: Ejecutar una operación en segundo plano

Cómo: Implementar un formulario que utiliza una operación en segundo plano

Referencia

BackgroundWorker