posts - 615, comments - 10492, trackbacks - 594, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

重新想象 Windows 8.1 Store Apps (89) - 通信的新特性: 下载数据, 上传数据, 上传文件

Posted on 2014-08-11 12:30 webabcd 阅读(...) 评论(...) 编辑 收藏

[源码下载]


重新想象 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("http://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
[源码下载]