C# – why the WebClient upload file code hangs

asp.netcnetvisual-studio-2008webclient

I am using VSTS 2008 + C# + .Net 3.5 + ASP.Net + IIS 7.0 to develop a Windows Forms application at client side to upload a file, and at server side I receive this file using an aspx file.

I find my client side application will hang after click the button to trigger upload event. Any ideas what is wrong and how to solve? Thanks!

Client side code,

  public partial class Form1 : Form
    {
        private static WebClient client = new WebClient();
        private static ManualResetEvent uploadLock = new ManualResetEvent(false);

        private static void Upload()
        {
            try
            {
                Uri uri = new Uri("http://localhost/Default2.aspx");
                String filename = @"C:\Test\1.dat";

                client.Headers.Add("UserAgent", "TestAgent");
                client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
                client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCompleteCallback);
                client.UploadFileAsync(uri, "POST", filename);
                uploadLock.WaitOne();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace.ToString());
            }
        }

        public static void UploadFileCompleteCallback(object sender, UploadFileCompletedEventArgs e)
        {
            Console.WriteLine("Completed! ");
            uploadLock.Set();
        }

        private static void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
        {
            Console.WriteLine("{0}    uploaded {1} of {2} bytes. {3} % complete...",
                (string)e.UserState,
                e.BytesSent,
                e.TotalBytesToSend,
                e.ProgressPercentage);

            // Console.WriteLine (e.ProgressPercentage);
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Upload();
        }
    }

Server side code:

    protected void Page_Load(object sender, EventArgs e)
    {
        string agent = HttpContext.Current.Request.Headers["UserAgent"];
        using (FileStream file = new FileStream(@"C:\Test\Agent.txt", FileMode.Append, FileAccess.Write))
        {
            byte[] buf = Encoding.UTF8.GetBytes(agent);
            file.Write(buf, 0, buf.Length);
        }

        foreach (string f in Request.Files.AllKeys)
        {
            HttpPostedFile file = Request.Files[f];
            file.SaveAs("C:\\Test\\UploadFile.dat");
        }
    }

Best Answer

you are waiting in the main windows events thread, so your GUI will be frozen.

Try this (using non static methods allows you to use the Control.Invoke method to run callbacks on the windows GUI thread and free this thread in order to redraw)

public partial class Form1 : Form
{
    private static WebClient client = new WebClient();
    private static ManualResetEvent uploadLock = new ManualResetEvent(false);

    private void Upload()
    {
        try
        {
            Cursor=Cursors.Wait;
            Uri uri = new Uri("http://localhost/Default2.aspx");
            String filename = @"C:\Test\1.dat";

            client.Headers.Add("UserAgent", "TestAgent");
            client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
            client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCompleteCallback);
             client.UploadFileAsync(uri, "POST", filename);    
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace.ToString());
            this.Cursor=Cursors.Default;
            this.Enabled=false;
        }
    }

    public void UploadFileCompleteCallback(object sender, UploadFileCompletedEventArgs e)
    {
      // this callback will be invoked by the async upload handler on a ThreadPool thread, so we cannot touch anything GUI-related. For this we have to switch to the GUI thread using control.BeginInvoke
      if(this.InvokeRequired)
      {
           // so this is called in the main GUI thread
           this.BeginInvoke(new UploadFileCompletedEventHandler(UploadFileCompleteCallback); // beginInvoke frees up the threadpool thread faster. Invoke would wait for completion of the callback before returning.
      }
      else
      {
          Cursor=Cursors.Default;
          this.enabled=true;
          MessageBox.Show(this,"Upload done","Done");
      }
public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Upload();
        }
    }
}

And do the same thing in your progress (you could update a progressbar indicator for example).

Cheers, Florian