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避免緩存問題,這也實在是一個沒有辦法的辦法,希望微軟在下一個版本中能夠改善這個問題。