重新想象 Windows 8.1 Store Apps (89) - 通信的新特性: 下载数据, 上传数据, 上传文件
重新想象 Windows 8.1 Store Apps (89) - 通信的新特性: 下载数据, 上传数据, 上传文件
作者:webabcd
介绍
重新想象 Windows 8.1 Store Apps 之通信的新特性
- 下载数据(显示下载进度,将下载数据保存到本地)
- 上传数据(显示上传进度)
- 上传文件
示例
HTTP 服务端
WebServer/HttpDemo.aspx.cs
/* * 用于响应 http 请求 */ using System; using System.IO; using System.Threading; using System.Web; namespace WebServer { public partial class HttpDemo : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // 停 3 秒,以方便测试 http 请求的取消 Thread.Sleep(3000); var action = Request.QueryString["action"]; switch (action) { case "getString": // 响应 http get string Response.Write("hello webabcd: " + DateTime.Now.ToString("hh:mm:ss")); break; case "getStream": // 响应 http get stream Response.Write("hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd"); break; case "postString": // 响应 http post string Response.Write(string.Format("param1:{0}, param2:{1}, referrer:{2}", Request.Form["param1"], Request.Form["param2"], Request.UrlReferrer)); break; case "postStream": // 响应 http post stream using (StreamReader reader = new StreamReader(Request.InputStream)) { if (Request.InputStream.Length > 1024 * 100) { // 接收的数据太大,则显示“数据接收成功” Response.Write("数据接收成功"); } else { // 显示接收到的数据 string body = reader.ReadToEnd(); Response.Write(Server.HtmlEncode(body)); } } break; case "uploadFile": // 处理上传文件的请求 for (int i = 0; i < Request.Files.Count; i++) { string key = Request.Files.GetKey(i); HttpPostedFile file = Request.Files.Get(key); string savePath = @"d:\" + file.FileName; // 保存文件 file.SaveAs(savePath); Response.Write(string.Format("key: {0}, fileName: {1}, savePath: {2}", key, file.FileName, savePath)); Response.Write("\n"); } break; case "outputCookie": // 用于显示服务端获取到的 cookie 信息 for (int i = 0; i < Request.Cookies.Count; i++) { HttpCookie cookie = Request.Cookies[0]; Response.Write(string.Format("cookieName: {0}, cookieValue: {1}", cookie.Name, cookie.Value)); Response.Write("\n"); } break; case "outputCustomHeader": // 用于显示一个自定义的 http header Response.Write("myRequestHeader: " + Request.Headers["myRequestHeader"]); break; default: break; } Response.End(); } } }
1、演示如何通过新的 HttpClient(Windows.Web.Http)获取下载进度,并将下载数据保存到本地
Download.xaml.cs
/* * 本例演示如何通过新的 HttpClient(Windows.Web.Http)获取下载进度,并将下载数据保存到本地 * * * 注:在 win8 时代要想获取下载进度只能依靠后台任务来完成 */ using System; using System.Threading; using System.Threading.Tasks; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using Windows.Web.Http; namespace Windows81.Communication.HTTP { public sealed partial class Download : Page { private HttpClient _httpClient; private CancellationTokenSource _cts; public Download() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } if (_cts != null) { _cts.Dispose(); _cts = null; } } private async void btnDownload_Click(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); _cts = new CancellationTokenSource(); try { // 用于获取下载进度 IProgress<HttpProgress> progress = new Progress<HttpProgress>(ProgressHandler); HttpResponseMessage response = await _httpClient.GetAsync( new Uri("https://files.cnblogs.com/webabcd/WindowsPhone.rar?ll"), HttpCompletionOption.ResponseContentRead).AsTask(_cts.Token, progress); // 把 progress 放到 task 里,以便获取下载进度 lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // 将下载好的数据保存到本地 StorageFolder storageFolder = KnownFolders.DocumentsLibrary; StorageFile storageFile = await storageFolder.CreateFileAsync("WindowsPhone.rar", CreationCollisionOption.ReplaceExisting); using (StorageStreamTransaction transaction = await storageFile.OpenTransactedWriteAsync()) { lblMsg.Text = "文件已下载,写入到磁盘中..."; /* * IHttpContent.WriteToStreamAsync() - 用于保存数据 */ await response.Content.WriteToStreamAsync(transaction.Stream); await transaction.CommitAsync(); lblMsg.Text = "文件已写入到磁盘"; } } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click(object sender, RoutedEventArgs e) { // 取消 http 请求 if (_cts != null) { _cts.Cancel(); _cts.Dispose(); _cts = null; } } // 下载进度发生变化时调用的处理器 private void ProgressHandler(HttpProgress progress) { /* * HttpProgress - http 通信的进度 * BytesReceived - 已收到的字节数 * BytesSent - 已发送的字节数 * TotalBytesToReceive - 总共需要收到的字节数 * TotalBytesToSend - 总共需要发送的字节数 * Retries - 重试次数 * Stage - 当前通信的阶段(HttpProgressStage 枚举) */ string result = "BytesReceived: {0}\nBytesSent: {1}\nRetries: {2}\nStage: {3}\nTotalBytesToReceive: {4}\nTotalBytesToSend: {5}\n"; result = string.Format(result, progress.BytesReceived, progress.BytesSent, progress.Retries, progress.Stage, progress.TotalBytesToReceive, progress.TotalBytesToSend); lblMsg.Text = result; } } }
2、演示如何通过新的 HttpClient(Windows.Web.Http)获取上传进度
Upload.xaml.cs
/* * 本例演示如何通过新的 HttpClient(Windows.Web.Http)获取上传进度 * * * 注:在 win8 时代要想获取上传进度只能依靠后台任务来完成 */ using System; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using Windows.Web.Http; namespace Windows81.Communication.HTTP { public sealed partial class Upload : Page { private HttpClient _httpClient; private CancellationTokenSource _cts; public Upload() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } if (_cts != null) { _cts.Dispose(); _cts = null; } } private async void btnUpload_Click(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); _cts = new CancellationTokenSource(); try { Uri resourceAddress = new Uri("http://localhost:39630/HttpDemo.aspx?action=postStream"); // 模拟一个比较大的比较慢的流,供 http 上传 const uint streamLength = 10000000; HttpStreamContent streamContent = new HttpStreamContent(new SlowInputStream(streamLength)); streamContent.Headers.ContentLength = streamLength; // 必须要指定请求数据的 ContentLength,否则就是 chunked 了 // 用于获取上传进度 IProgress<HttpProgress> progress = new Progress<HttpProgress>(ProgressHandler); HttpResponseMessage response = await _httpClient.PostAsync(resourceAddress, streamContent).AsTask(_cts.Token, progress); // 把 progress 放到 task 里,以便获取上传进度 lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } // 生成一个指定大小的内存流 private static MemoryStream GenerateSampleStream(int size) { byte[] subData = new byte[size]; for (int i = 0; i < subData.Length; i++) { subData[i] = (byte)(97 + i % 26); // a-z } return new MemoryStream(subData); } private void btnCancel_Click(object sender, RoutedEventArgs e) { // 取消 http 请求 if (_cts != null) { _cts.Cancel(); _cts.Dispose(); _cts = null; } } // 上传进度发生变化时调用的处理器 private void ProgressHandler(HttpProgress progress) { /* * HttpProgress - http 通信的进度 * BytesReceived - 已收到的字节数 * BytesSent - 已发送的字节数 * TotalBytesToReceive - 总共需要收到的字节数 * TotalBytesToSend - 总共需要发送的字节数 * Retries - 重试次数 * Stage - 当前通信的阶段(HttpProgressStage 枚举) */ string result = "BytesReceived: {0}\nBytesSent: {1}\nRetries: {2}\nStage: {3}\nTotalBytesToReceive: {4}\nTotalBytesToSend: {5}\n"; result = string.Format(result, progress.BytesReceived, progress.BytesSent, progress.Retries, progress.Stage, progress.TotalBytesToReceive, progress.TotalBytesToSend); lblMsg.Text = result; } } // 模拟一个比较慢的输入流 class SlowInputStream : IInputStream { uint length; uint position; public SlowInputStream(uint length) { this.length = length; position = 0; } public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) { return AsyncInfo.Run<IBuffer, uint>(async (cancellationToken, progress) => { if (length - position < count) { count = length - position; } byte[] data = new byte[count]; for (uint i = 0; i < count; i++) { data[i] = 64; } // 延迟 10 毫秒再继续,以模拟一个比较慢的输入流 await Task.Delay(10); position += count; progress.Report(count); return data.AsBuffer(); }); } public void Dispose() { } } }
3、演示如何通过新的 HttpClient(Windows.Web.Http)上传文件(通过 multipart/form-data 的方式)
UploadFile.xaml.cs
/* * 本例演示如何通过新的 HttpClient(Windows.Web.Http)上传文件(通过 multipart/form-data 的方式) */ using System; using System.Threading; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using Windows.Web.Http; namespace Windows81.Communication.HTTP { public sealed partial class UploadFile : Page { private HttpClient _httpClient; private CancellationTokenSource _cts; public UploadFile() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } if (_cts != null) { _cts.Dispose(); _cts = null; } } private async void btnUploadFile_Click(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); _cts = new CancellationTokenSource(); try { // 构造需要上传的文件数据 StorageFile file1 = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Son.jpg", UriKind.Absolute)); IRandomAccessStreamWithContentType stream1 = await file1.OpenReadAsync(); HttpStreamContent streamContent1 = new HttpStreamContent(stream1); // 构造需要上传的文件数据 StorageFile file2 = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Son.jpg", UriKind.Absolute)); IRandomAccessStreamWithContentType stream2 = await file1.OpenReadAsync(); HttpStreamContent streamContent2 = new HttpStreamContent(stream2); // 通过 HttpMultipartFormDataContent 来指定需要“multipart/form-data”上传的文件 HttpMultipartFormDataContent fileContent = new HttpMultipartFormDataContent(); // 第 1 个参数:需要上传的文件数据 // 第 2 个参数:对应 asp.net 服务的 Request.Files 中的 key(参见:WebServer 项目中的 HttpDemo.aspx.cs) // 第 3 个参数:对应 asp.net 服务的 Request.Files 中的 fileName(参见:WebServer 项目中的 HttpDemo.aspx.cs) fileContent.Add(streamContent1, "file1", "file1.jpg"); fileContent.Add(streamContent2, "file2", "file2.jpg"); HttpResponseMessage response = await _httpClient.PostAsync(new Uri("http://localhost:39630/HttpDemo.aspx?action=uploadFile"), fileContent).AsTask(_cts.Token); lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click(object sender, RoutedEventArgs e) { // 取消 http 请求 if (_cts != null) { _cts.Cancel(); _cts.Dispose(); _cts = null; } } } }
OK
[源码下载]