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()
返回beginfo()
的回调方法不能保证从该线程调用。这实际上很容易处理:
- 每个控件都有一个
属性,该属性是从错误线程调用的唯一安全属性。如果它返回invokererequired
,您就知道您在一个不安全的线程上李>true
- 每个控件都有一个
方法,该方法接受委托参数,并将在创建控件的线程上调用该委托Invoke()
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;