UploadFileAsync不是异步的?

时间:2010-05-02 10:01:54

标签: c# webclient

Aight,做了一些谷歌搜索并在这里搜索,我发现的唯一问题是this,虽然它唯一的答案没有被标记为已被接受,但已经过时且令人困惑。

我的问题基本上就是我在标题中所说的。发生的事情是GUI在上传过程中冻结。我的代码:

// stuff above snipped

public partial class Form1 : Form
{
    WebClient wcUploader = new WebClient();

    public Form1()
    {
        InitializeComponent();

        wcUploader.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCompletedCallback);
        wcUploader.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            string toUpload = openFileDialog1.FileName;
            wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload);
        }
    }

    void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
    {
        textBox1.Text = System.Text.Encoding.UTF8.GetString(e.Result);
    }

    void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
    {
        textBox1.Text = (string)e.UserState + "\n\n"
            + "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend + "b (" + e.ProgressPercentage + "%)";
    }
}

编辑:为了澄清,这是按顺序发生的事情:

  1. 我点击button1
  2. 我选择了一个文件
  3. GUI停止响应,就像点击它时没有任何反应
  4. 几秒钟后,50%的人出现在文本框中 Aaand实现命中。请参阅我对我标记为解决方案的问题的评论
  5. 大约一秒钟之后,GUI没有响应,它被替换为响应

2 个答案:

答案 0 :(得分:4)

当然可以。

代码工作正常。

wcUploader.UploadFileAsync(...)启动请求并继续执行,同时在TextBox1中更新进度,完成后我得到一些JSON。

那是异步。如果你只是简单地调用了wcUploader.UploadFile,那么执行将阻止那里直到文件被上传,你将没有进展事件。

底线:

未阻止UI,调用进度事件并实时更新UI。

更新

要在webclient建立http连接时消除初始阻止,只需在另一个线程上调用上传。在这种情况下,您必须使用调用来防止跨线程异常:

using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private readonly WebClient wcUploader = new WebClient();

        public Form1()
        {
            InitializeComponent();

            wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
            wcUploader.UploadProgressChanged += UploadProgressCallback;
        }


        private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
        {
            // a clever way to handle cross-thread calls and avoid the dreaded
            // "Cross-thread operation not valid: Control 'textBox1' accessed 
            // from a thread other than the thread it was created on." exception

            // this will always be called from another thread,
            // no need to check for InvokeRequired
            BeginInvoke(
                new MethodInvoker(() =>
                    {
                        textBox1.Text = Encoding.UTF8.GetString(e.Result);
                        button1.Enabled = true;
                    }));
        }

        private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
        {
            // a clever way to handle cross-thread calls and avoid the dreaded
            // "Cross-thread operation not valid: Control 'textBox1' accessed 
            // from a thread other than the thread it was created on." exception

            // this will always be called from another thread,
            // no need to check for InvokeRequired

            BeginInvoke(
                new MethodInvoker(() =>
                    {
                        textBox1.Text = (string)e.UserState + "\n\n"
                                        + "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
                                        + "b (" + e.ProgressPercentage + "%)";
                    }));
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                button1.Enabled = false;
                string toUpload = openFileDialog1.FileName;
                textBox1.Text = "Initiating connection";
                new Thread(() =>
                           wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
            }
        }
    }
}

答案 1 :(得分:1)

无论UI锁定如何,您的代码中都存在一个值得修复的错误:

指定异步上传器应触发的两个回调。在那些回调中,你将在上传者的线程上运行;但是,您只能从主GUI线程触摸GUI - 因此您的回调可能会破坏GUI的状态。

在任一回调中都不应触及textBox1.Text。这不太可能是问题,但是,你应该修复它以避免崩溃和腐败错误。您链接的问题说明了避免这种情况的一种方法:检查表单的Control.InvokeRequired属性(在幕后检查您是否在正确的线程上),或者只是假设需要调用然后 - 使用{ {1}}在GUI线程上触发方法。

您的任何控件都会执行,因为它们都在同一个线程中运行;所以Control.BeginInvokeif (textBox1.InvokeRequired) textBox1.BeginInvoke...

一样好