工作这么长时间,起初还是喜欢用面向程序过程的思路去写代码。
慢慢的才会用面向对象的思路分析、解决问题。也算是一点点进步吧。
最近在做一个下载音乐的功能。用到了HttpClient类。
于是就简单的写了一个文件处理类。主要实现了Get请求,Post还很不完善(Post看到过别人写的很不错的类,以后会贴出)。
还有能够实时的显示下载进度,中断下载。
贴出代码,在代码里解释:
public class HttpRequest { #region Instance Field private readonly string _url; //请求的url private readonly string _body; //Post/Get时的数据 private HttpClient _httpClient; private CancellationTokenSource _cts; //用于取消请求 private IProgress<HttpProgress> _httpProgressDownload; //用于下载进度 private IProgress<HttpProgress> _httpProgressUpload; private double progressUpload = 0; private double progressDownload = 0; //下载进度 #endregion #region Delegates public delegate void OnFailedEventHandle(string error, WebExceptionStatus status); public delegate void OnSucceedEventHandle(InMemoryRandomAccessStream randomAccessStream); public delegate void OnCancelEventHandle(string message); public delegate void OnProgressChangedEventHandle(double progress); #endregion #region Events //事件 分别用来处理获取失败、成功、取消、进度信息 public event OnFailedEventHandle FailedEvent; public event OnSucceedEventHandle SucceedEvent; public event OnCancelEventHandle CancelEvent; public event OnProgressChangedEventHandle ProgressChangedEvent; #endregion //构造函数 public HttpRequest(string url, string body = null) { this._url = url; this._body = body; _httpClient = new HttpClient(); _cts = new CancellationTokenSource(); } //开始运行
public void Run() { DoHttpClientRequest(); } public async void DoHttpClientRequest() {
//根据是否存在body判断是Get请求还是Post请求 RequestType method = string.IsNullOrEmpty(_body) ? RequestType.Get : RequestType.Post; var request = CreateHttp(_url, method); if (_httpClient != null) { try { HttpResponseMessage response = null; if (method == RequestType.Post) { //POST //_httpProgressUpload = new Progress<HttpProcess>(ProgressUploadHandler); //response = await _httpClient.SendRequestAsync(request).AsTask(_cts.Token, _progressUpload); response = await _httpClient.SendRequestAsync(request).AsTask(_cts.Token); } else if (method == RequestType.Get) { //GET
//下载进度状态信息 _httpProgressDownload = new Progress<HttpProgress>(ProgressDownloadHandler); try { response = await _httpClient.SendRequestAsync(request).AsTask(_cts.Token, _httpProgressDownload); //HttpCompletionOption.ResponseHeadersRead多了这个参数 在接受到头之后完成。 于是就不继续进行了
//response = await _httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead).AsTask(_cts.Token, _httpProgressDownload);
_cts.Token.ThrowIfCancellationRequested();
//处理流 using (Stream responseStream = (await response.Content.ReadAsInputStreamAsync()).AsStreamForRead()) { //将Stream转换为IRandomAccessStream var randomAccessStream = new InMemoryRandomAccessStream(); var outputStream = randomAccessStream.GetOutputStreamAt(0); await RandomAccessStream.CopyAsync(responseStream.AsInputStream(), outputStream); if (randomAccessStream != null) { if (SucceedEvent != null) SucceedEvent(randomAccessStream); //获取到源的回调方法,并返回获取的内容 } } }
//中断Task时候会抛出异常,所以要通过try catch这种方法来获取是否终止。 catch (TaskCanceledException) { //请求被取消 CancelEvent("下载已停止"); } } } catch (WebException e) { FailedEvent(e.Message, e.Status); } } } public HttpRequestMessage CreateHttp(string url, RequestType type = RequestType.Get) { HttpRequestMessage request = null; try { if (type == RequestType.Get) { request = new HttpRequestMessage(HttpMethod.Get, new Uri(url, UriKind.Absolute)); } else { request = new HttpRequestMessage(HttpMethod.Post, new Uri(url, UriKind.Absolute)); request.Content = SetPostContent(this._body); }
SetHeaders(); } catch (WebException e) { FailedEvent(e.Message, e.Status); } return request; }
//Post请求内容 public HttpStreamContent SetPostContent(string body) { byte[] subData = new byte[body.Length]; MemoryStream stream = new MemoryStream(subData); HttpStreamContent streamContent = new HttpStreamContent(stream.AsInputStream()); return streamContent; }
public void SetHeaders()
{
//略
} public void ProgressDownloadHandler(HttpProgress progress) {
//处理进度 包括了很多状态 如ConnectingToServer、WaitingForResponse等 string infoState = progress.Stage.ToString(); double totalByteToRecive = 0; if (progress.TotalBytesToSend.HasValue) { //要发送的数据 } if (progress.TotalBytesToReceive.HasValue) { //接收数据 获取总接收数据 totalByteToRecive = progress.TotalBytesToReceive.Value; } if (progress.Stage == HttpProgressStage.ReceivingContent) { progressUpload = progress.BytesReceived / totalByteToRecive; if (ProgressChangedEvent != null) { ProgressChangedEvent(progressUpload * 100); } } } public void Cancel() { if (_cts.Token.CanBeCanceled) {
//取消请求并且释放资源 _cts.Cancel(); _cts.Dispose(); } } } //枚举变量 来判断是Get请求还是Post请求 public enum RequestType { Post, Get }
后台代码:
1 url = "http://mxd.766.com/sdo/music/data/1/m1.mp3" 2 HttpRequest httpRequest = new HttpRequest(url); 3 4 httpRequest.Run(); 5 httpRequest.SucceedEvent += async (result) => 6 { 7 try 8 { 9 await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 10 { 11 //设置源 12 MediaControl.SetDownloadSource(result); 13 }); 14 } 15 catch(Exception e) 16 { 17 18 } 19 //保存文件到音乐 20 IBuffer buffer = new Windows.Storage.Streams.Buffer((uint)result.Size); 21 await result.ReadAsync(buffer, (uint)result.Size, InputStreamOptions.None); 22 await StorageHelper.SaveToStorage(this.Classify.Name, selectItem.Name + ".mp3", buffer); 23 24 CommonHelper.ShowToast(selectItem.Name + ".mp3 下载成功"); 25 }; 26 httpRequest.FailedEvent += async (ss, ee) => 27 { 28 await new MessageDialog("获取音乐失败").ShowAsync(); 29 }; 30 httpRequest.CancelEvent += async (ss1) => 31 { 32 await new MessageDialog(ss1).ShowAsync(); 33 }; 34 httpRequest.ProgressChangedEvent += (progress) => 35 { 36 selectItem.DownProgress = progress; 37 //progress去绑定对象,就能够实时的显示进度 38 };
这样就能够实现下载、中断了。 我发现,在中断后再点击下载,进度条还是会接着走的。
这里并没有主动的去实现续传。
注:HttpClient类发起的网络请求都是基于任务的异步方法,所以要取消其异步的操作可以通过异步任务的取消对象CancellationTokenSource对象来取消。
如果使用CancellationTokenSource对象来取消异步的请求会触发TaskCanceledException异常,这个异常需要我们用try
catch语句来捕获,便可以识别到请求是被取消的。