WebClient实际上是一个在HttpWebRequest之上的库,它的主要优点就是使用起来比较简单,但是它有两个致命缺陷,一是程序的回调在UI线程执行,会导致程序性能下降。另外一个,他没有实现本地缓存的控制策略。
那么HttpWebRequest呢,虽然它仍然无法支持GET方法下的本地缓存策略控制,但是它的性能实在是要好的太多,而且由于HttpWebRequest是个比WebClient更低级的库,你可以更好的对传输进行控制,所以我强烈推荐大家用HttpWebRequest,至少在微软对WebClient进行了改善之前是这样。
应为HttpWebRequest的异步回调函数会在自己的线程中执行,所以随之而来会带来一个问题,就是跨线程调用问题,这个问题可以通过这个方式,调用:
System.Windows.Deployment.Current.Dispatcher.BeginInvoke方法来在主线程中执行代码,这样就可以解决这个问题,并且我们可以通过封装HttpWebRequest来得到一个与WebClient使用方法类似的新类,下面我就把在项目中实现的这个类的代码给大家看看
public class DownloadStringCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public DownloadStringCompletedEventArgs(string result, bool cancelled, Exception error, object userState)
: base(error, cancelled, userState)
{
Result = result;
}
public string Result
{
get;
private set;
}
}
public class OpenReadCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public OpenReadCompletedEventArgs(Stream result,bool cancelled,Exception error,object userState)
: base(error, cancelled, userState)
{
Result = result;
}
public Stream Result
{
get;
private set;
}
}
public delegate void DownloadStringCompletedEventHandler(Object sender, DownloadStringCompletedEventArgs e);
public delegate void OpenReadCompletedEventHandler(object sender, OpenReadCompletedEventArgs e);
public class NoCacheWebClient
{
HttpWebRequest _webRequest = null;
private string _result = "";
private byte[] _buffer;
const int BUFFER_SIZE = 1024;
MemoryStream _memStream = null;
object _userToken = null;
static string cache = Guid.NewGuid().ToString();
public NoCacheWebClient()
{
_buffer = new byte[BUFFER_SIZE];
Encoding = Encoding.UTF8;
_memStream = new MemoryStream();
}
public event DownloadStringCompletedEventHandler DownloadStringCompleted;
public event OpenReadCompletedEventHandler OpenReadCompleted;
public void CancelAsync()
{
if (_webRequest != null)
{
_webRequest.Abort();
_result = "";
_userToken = null;
}
}
public Encoding Encoding
{
get;
set;
}
private Uri RequestUri
{
set;
get;
}
public void OpenReadAsync(Uri address)
{
RequestUri = address;
_webRequest = (HttpWebRequest)HttpWebRequest.Create(address.AbsoluteUri);
_webRequest.BeginGetResponse(StreamRespCallback, null);
}
private void StreamRespCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebResponse response = (HttpWebResponse)_webRequest.EndGetResponse(asynchronousResult);
Stream responseStream = response.GetResponseStream();
if (OpenReadCompleted != null)
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(OpenReadCompleted, new object[] { this, new OpenReadCompletedEventArgs(responseStream, false, null, _userToken) });
}
}
catch (WebException e)
{
string message = e.Message;
}
}
public void DownloadStringAsync(Uri address)
{
_result = "";
_memStream = new MemoryStream();
string cacheValue = "";
if (address.Query.Length == 0)
{
cacheValue = "?Cache=" + cache;
}
else
{
cacheValue = "&Cache=" + cache;
}
Uri newAddress = new Uri(address.AbsoluteUri + cacheValue, UriKind.RelativeOrAbsolute);
RequestUri = newAddress;
_webRequest = (HttpWebRequest)HttpWebRequest.Create(newAddress.AbsoluteUri);
_webRequest.BeginGetResponse(RespCallback, null);
}
public void DownloadStringAsync(Uri address,object userToken)
{
_result = "";
_userToken = userToken;
_memStream = new MemoryStream();
string cacheValue = "";
if (address.Query.Length == 0)
{
cacheValue = "?Cache=" + cache;
}
else
{
cacheValue = "&Cache=" + cache;
}
Uri newAddress = new Uri(address.AbsoluteUri + cacheValue, UriKind.RelativeOrAbsolute);
RequestUri = newAddress;
_webRequest = (HttpWebRequest)HttpWebRequest.Create(newAddress.AbsoluteUri);
_webRequest.BeginGetResponse(RespCallback, userToken);
}
private void RespCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebResponse response = (HttpWebResponse)_webRequest.EndGetResponse(asynchronousResult);
Stream responseStream = response.GetResponseStream();
responseStream.BeginRead(_buffer, 0, BUFFER_SIZE, new AsyncCallback(ReadCallback), responseStream);
}
catch (WebException e)
{
string message = e.Message;
}
}
private void ReadCallback(IAsyncResult asyncResult)
{
try
{
Stream stream = (Stream)asyncResult.AsyncState;
int read = stream.EndRead(asyncResult);
if (read > 0)
{
_memStream.Write(_buffer, 0, read);
stream.BeginRead(_buffer, 0, BUFFER_SIZE, new AsyncCallback(ReadCallback), stream);
}
else
{
_result = Encoding.GetString(_memStream.ToArray(), 0, (int)_memStream.Length);
stream.Close();
_memStream.Close();
if (DownloadStringCompleted != null)
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(DownloadStringCompleted, new object[] { this, new DownloadStringCompletedEventArgs(_result, false, null, _userToken) });
}
}
}
catch (System.Exception ex)
{
string message = ex.Message;
}
}
private void WriteCallBack(IAsyncResult asyncResult)
{
try
{
_memStream.EndWrite(asyncResult);
Stream stream = (Stream)asyncResult.AsyncState;
stream.BeginRead(_buffer, 0, BUFFER_SIZE, new AsyncCallback(ReadCallback), stream);
}
catch (System.Exception ex)
{
string message = ex.Message;
}
}
}
这个类的使用方法基本与WebClient类似,当然我只实现了部分函数和属性,而且这个程序会通过设置guid来强制刷新url避免缓存问题,这也实在是一个没有办法的办法,希望微软在下一个版本中能够改善这个问题。