Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/facebook/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用HttpWebRequest类在上载和下载时以百分比显示进度_C#_Httpwebrequest_Progress - Fatal编程技术网

C# 使用HttpWebRequest类在上载和下载时以百分比显示进度

C# 使用HttpWebRequest类在上载和下载时以百分比显示进度,c#,httpwebrequest,progress,C#,Httpwebrequest,Progress,我正试图使用C#中的HttpWebRequest将(在同一请求中)上传和下载到服务器,由于数据量很大(考虑到网络速度),我想向用户显示作业完成了多远,剩下多少(不是秒而是百分比) 我已经阅读了几个试图实现这一点的示例,但没有一个显示任何进度条。它们都只是使用async在用户界面上传/下载时不阻塞它。他们大多专注于上传/下载,没有人尝试在同一个请求中包含这两个内容 因为我使用.NET4作为目标框架,所以我自己无法实现async方法。如果您要建议任何异步操作,请使用Begin…方法,而不是wait关

我正试图使用
C#
中的
HttpWebRequest
将(在同一请求中)上传和下载到服务器,由于数据量很大(考虑到网络速度),我想向用户显示作业完成了多远,剩下多少(不是秒而是百分比)

我已经阅读了几个试图实现这一点的示例,但没有一个显示任何进度条。它们都只是使用
async
在用户界面上传/下载时不阻塞它。他们大多专注于上传/下载,没有人尝试在同一个请求中包含这两个内容


因为我使用.NET4作为目标框架,所以我自己无法实现
async
方法。如果您要建议任何异步操作,请使用
Begin…
方法,而不是
wait
关键字!谢谢。

您需要知道一些事情才能成功地完成此任务

步骤0:将.NET 4.0的文档放在手边:

如果查看
HttpWebRequest
文档,您将看到
GetResponse()
有两个名称类似的方法:
BeginGetResponse()
EndGetResponse()
。这些方法使用最古老的.NET异步模式,称为“IAsyncResult模式”。关于此模式有数千页的文本,如果您需要详细信息,可以阅读教程。以下是速成课程:

  • 对于方法
    Foo()
    ,有一个
    beginfo()
    ,它可能需要参数,并且必须返回IAsyncResult实现。此方法执行任务
    Foo()
    执行,但不会阻塞当前线程
  • 如果
    beginfo()
    接受一个AsyncCallback参数,它希望您提供一个委托,该委托在完成时将被调用。(这是知道它完成的几种方法之一,但恰好是
    HttpWebRequest
    使用的技术。)
  • EndFoo()
    BeginFoo()
    返回的
    IAsyncResult
    作为参数,并返回相同的内容
    Foo()
    返回
  • 接下来,你应该意识到一个陷阱。控件有一个名为“thread affinity”的属性,这意味着只有在创建控件的线程上工作时,才能与控件交互。这一点很重要,因为您提供给
    beginfo()
    的回调方法不能保证从该线程调用。这实际上很容易处理:

    • 每个控件都有一个
      invokererequired
      属性,该属性是从错误线程调用的唯一安全属性。如果它返回
      true
      ,您就知道您在一个不安全的线程上
    • 每个控件都有一个
      Invoke()
      方法,该方法接受委托参数,并将在创建控件的线程上调用该委托
    现在,让我们开始看看我在一个简单的WinForms应用程序中编写的一些代码,这些代码用于报告下载Google主页的进度。这应该类似于解决问题的代码。它不是最好的代码,但它演示了概念。表单有一个名为
    progressBar1
    的进度条,我通过单击按钮调用了
    GetWebContent()

    HttpWebRequest _request;
    IAsyncResult _responseAsyncResult;
    
    private void GetWebContent() {
        _request = WebRequest.Create("http://www.google.com") as HttpWebRequest;
        _responseAsyncResult = _request.BeginGetResponse(ResponseCallback, null);           
    }
    
    此代码启动异步版本的
    GetResponse()
    。我们需要在字段中存储请求和
    IAsyncResult
    ,因为
    ResponseCallback()
    需要调用
    EndGetResponse()
    GetWebContent()
    中的所有内容都在UI线程上,因此如果您想更新某些控件,可以在此处安全地进行更新。接下来,
    ResponseCallback()

    AsyncCallback委托签名强制它接受
    对象
    参数,但我不使用它。它使用前面得到的
    IAsyncResult
    调用
    EndGetResponse()
    ,现在我们可以继续,就好像我们没有使用异步调用一样但是因为这是一个异步回调,它可能在工作线程上执行,所以不要在这里直接更新任何控件

    无论如何,它从响应中获取内容长度,如果您想要计算下载进度,则需要该长度。有时提供此信息的标题不存在,您会得到-1。这意味着您需要自己进行进度计算,您必须找到其他方法来了解正在下载的文件的总大小。在我的例子中,将变量设置为某个值就足够了,因为我不关心数据本身

    之后,它获取表示响应的流,并将其传递给执行下载和进度报告的助手方法:

    private byte[] GetContentWithProgressReporting(Stream responseStream, long contentLength) {
        UpdateProgressBar(0);
    
        // Allocate space for the content
        var data = new byte[contentLength];
        int currentIndex = 0;
        int bytesReceived = 0;
        var buffer = new byte[256];
        do {
            bytesReceived = responseStream.Read(buffer, 0, 256);
            Array.Copy(buffer, 0, data, currentIndex, bytesReceived);
            currentIndex += bytesReceived;
    
            // Report percentage
            double percentage = (double)currentIndex / contentLength;
            UpdateProgressBar((int)(percentage * 100));
        } while (currentIndex < contentLength);
    
        UpdateProgressBar(100);
        return data;
    }
    
    如果
    InvokeRequired
    返回true,它将通过
    Invoke()
    调用自身。如果它恰好位于正确的线程上,它将更新进度条。如果您碰巧正在使用WPF,则有类似的方法来封送调用,但我相信它们是通过
    Dispatcher
    对象进行的

    我知道那是很多。这就是为什么新的
    async
    功能如此强大的部分原因。
    IAsyncResult
    模式功能强大,但不会从您身上提取很多细节。但即使在较新的模式中,您也必须跟踪何时更新进度条是安全的

    需要考虑的事项:

    • 这是示例代码。为了简洁起见,我没有包含任何异常处理
    • 如果异常是由
      GetResponse()
      引发的,则在调用
      EndGetResponse()
      时将引发该异常
    • 如果回调中发生异常,而您尚未处理它,它将终止其线程,但不会终止您的应用程序。对于一个新手异步程序员来说,这可能既奇怪又神秘
    • 注意我说的话
      private byte[] GetContentWithProgressReporting(Stream responseStream, long contentLength) {
          UpdateProgressBar(0);
      
          // Allocate space for the content
          var data = new byte[contentLength];
          int currentIndex = 0;
          int bytesReceived = 0;
          var buffer = new byte[256];
          do {
              bytesReceived = responseStream.Read(buffer, 0, 256);
              Array.Copy(buffer, 0, data, currentIndex, bytesReceived);
              currentIndex += bytesReceived;
      
              // Report percentage
              double percentage = (double)currentIndex / contentLength;
              UpdateProgressBar((int)(percentage * 100));
          } while (currentIndex < contentLength);
      
          UpdateProgressBar(100);
          return data;
      }
      
      private void UpdateProgressBar(int percentage) {
          // If on a worker thread, marshal the call to the UI thread
          if (progressBar1.InvokeRequired) {
              progressBar1.Invoke(new Action<int>(UpdateProgressBar), percentage);
          } else {
              progressBar1.Value = percentage;
          }
      }
      
          private byte[] GetContentWithProgressReporting(Stream responseStream, long contentLength) {
      UpdateProgressBar(0);
      
      // Allocate space for the content
      var data = new byte[contentLength];
      int currentIndex = 0;
      int bytesReceived = 0;
      int totalBytesReceived = 0;
      var buffer = new byte[256];
      do {
          bytesReceived = responseStream.Read(buffer, 0, 256);
          Array.Copy(buffer, 0, data, currentIndex, bytesReceived);
          currentIndex += bytesReceived;
          totalBytesReceived += bytesReceived;
      
          // Report percentage
          double percentage = (double)currentIndex / contentLength;
          UpdateProgressBar((int)(percentage * 100));
      
      
      
      } while (totalBytesReceived < contentLength);
      
      //DEBUGGING:  MessageBox.Show("GetContentWithProgressReporting Method - Out of loop");
      UpdateProgressBar(100);
      return data;