2011-10-20 17 views
0

其他开发人员!具有取消支持(CancellationTokenSource)和进度报告的PostSubmitter的异步CTP

我有一个使用POST或GET发布到网站的类并阅读了回复。现在都是异步并且不会导致UI挂起。

我需要升级它来处理现在取消。所有正在使用的Async方法都不接受取消标记。我需要了解为什么和我的选择是什么。如果可能,我应该在类中创建CancellationTokenSource对象还是从UI中对其进行参数化?其次,我需要公开PostData()方法的进度。我会怎么做?

类:

using System; 
using System.Collections.Specialized; 
using System.IO; 
using System.Net; 
using System.Text; 
using System.Threading.Tasks; 
using System.Web; 
using System.Windows.Forms; 
using System.Collections.Generic; 
using RESTClient.Core.UploadFile; 
using System.Threading; 

namespace RESTClient.Core { 

    /// <summary> 
    /// Submits post data to a url. 
    /// </summary> 
    public class PostSubmitter { 

     #region Backing Store 
     private string _URL = string.Empty; 
     private NameValueCollection _PostValues = new NameValueCollection(); 
     private PostTypeEnum _PostType = PostTypeEnum.GET; 
     #endregion 

     #region Constructors 
     /// <summary> 
     /// Default constructor. 
     /// </summary> 
     public PostSubmitter() { 

     } 

     /// <summary> 
     /// Constructor that accepts a url as a parameter 
     /// </summary> 
     /// <param name="url">The url where the post will be submitted to.</param> 
     public PostSubmitter(string url) 
      : this() { 
      _URL = url; 
     } 

     /// <summary> 
     /// Constructor allowing the setting of the url and items to post. 
     /// </summary> 
     /// <param name="url">the url for the post.</param> 
     /// <param name="values">The values for the post.</param> 
     public PostSubmitter(string url, NameValueCollection values) 
      : this(url) { 
      _PostValues = values; 
     } 
     #endregion 

     #region Properties 
     /// <summary> 
     /// Gets or sets the url to submit the post to. 
     /// </summary> 
     public string Url { 
      get { 
       return _URL; 
      } 
      set { 
       _URL = value; 
      } 
     } 

     /// <summary> 
     /// Gets or sets the name value collection of items to post. 
     /// </summary> 
     public NameValueCollection PostItems { 
      get { 
       return _PostValues; 
      } 
      set { 
       _PostValues = value; 
      } 
     } 

     /// <summary> 
     /// Gets or sets the type of action to perform against the url. 
     /// </summary> 
     public PostTypeEnum Type { 
      get { 
       return _PostType; 
      } 
      set { 
       _PostType = value; 
      } 
     } 
     #endregion 

     /// <summary> 
     /// Posts the supplied data to specified url. 
     /// </summary> 
     /// <returns>a string containing the result of the post.</returns> 
     public async Task<String> Post() { 
      StringBuilder parameters = new StringBuilder(); 
      for (int i = 0; i < _PostValues.Count; i++) { 
       EncodeAndAddItem(ref parameters, _PostValues.GetKey(i), _PostValues[i]); 
      } 
      string result = await PostData(_URL, parameters.ToString()); 
      return result; 
     } 

     /// <summary> 
     /// Posts the supplied data to specified url. 
     /// </summary> 
     /// <param name="url">The url to post to.</param> 
     /// <returns>a string containing the result of the post.</returns> 
     public async Task<String> Post(string url) { 
      _URL = url; 
      return await this.Post(); 
     } 

     /// <summary> 
     /// Posts the supplied data to specified url. 
     /// </summary> 
     /// <param name="url">The url to post to.</param> 
     /// <param name="values">The values to post.</param> 
     /// <returns>a string containing the result of the post.</returns> 
     public async Task<String> Post(string url, NameValueCollection values) { 
      _PostValues = values; 
      return await this.Post(url); 
     } 

     /// <summary> 
     /// Posts data to a specified url. Note that this assumes that you have already url encoded the post data. 
     /// </summary> 
     /// <param name="postData">The data to post.</param> 
     /// <param name="url">the url to post to.</param> 
     /// <returns>Returns the result of the post.</returns> 
     private async Task<String> PostData(string url, string postData) { 
      HttpWebRequest request = null; 

      if (_PostType == PostTypeEnum.POST) { 
       Uri uri = new Uri(url); 
       request = WebRequest.Create(uri) as HttpWebRequest; 
       request.Method = "POST"; 
       request.ContentType = "application/x-www-form-urlencoded"; 
       request.ContentLength = postData.Length; 

       using (Stream writeStream = await request.GetRequestStreamAsync()) { 
        UTF8Encoding encoding = new UTF8Encoding(); 
        byte[] bytes = encoding.GetBytes(postData); 
        writeStream.Write(bytes, 0, bytes.Length); 
       } 
      } 
      else { 
       Uri uri = new Uri(url + "?" + postData); 
       request = WebRequest.Create(uri) as HttpWebRequest; 
       request.Method = "GET"; 
      } 

      using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) { 
       using (Stream responseStream = response.GetResponseStream()) { 
        using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8)) { 
         return await readStream.ReadToEndAsync(); 
        } 
       } 
      } 
     } 

     /// <summary> 
     /// Encodes an item and ads it to the string. 
     /// </summary> 
     /// <param name="baseRequest">The previously encoded data.</param> 
     /// <param name="dataItem">The data to encode.</param> 
     /// <returns>A string containing the old data and the previously encoded data.</returns> 
     private void EncodeAndAddItem(ref StringBuilder baseRequest, string key, string dataItem) { 
      if (baseRequest == null) { 
       baseRequest = new StringBuilder(); 
      } 
      if (baseRequest.Length != 0) { 
       baseRequest.Append("&"); 
      } 
      baseRequest.Append(key); 
      baseRequest.Append("="); 
      baseRequest.Append(HttpUtility.UrlEncode(dataItem)); 
     } 

     public async void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) { 
      //log.Debug(string.Format("Uploading {0} to {1}", file, url)); 
      string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); 
      byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); 

      HttpWebRequest wr = WebRequest.Create(url) as HttpWebRequest; 
      wr.ContentType = "multipart/form-data; boundary=" + boundary; 
      wr.Method = "POST"; 
      wr.KeepAlive = true; 
      wr.Credentials = CredentialCache.DefaultCredentials; 

      Stream rs = await wr.GetRequestStreamAsync(); 

      string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; 
      foreach (string key in nvc.Keys) { 
       await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length); 
       string formitem = string.Format(formdataTemplate, key, nvc[key]); 
       byte[] formitembytes = Encoding.UTF8.GetBytes(formitem); 
       await rs.WriteAsync(formitembytes, 0, formitembytes.Length); 
      } 
      await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length); 

      string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; 
      string header = string.Format(headerTemplate, paramName, file, contentType); 
      byte[] headerbytes = Encoding.UTF8.GetBytes(header); 
      rs.WriteAsync(headerbytes, 0, headerbytes.Length); 

      FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); 
      byte[] buffer = new byte[4096]; 
      int bytesRead = 0; 
      while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0) { 
       await rs.WriteAsync(buffer, 0, bytesRead); 
      } 
      fileStream.Close(); 

      byte[] trailer = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); 
      await rs.WriteAsync(trailer, 0, trailer.Length); 
      rs.Close(); 

      WebResponse wresp = null; 
      try { 
       wresp = await wr.GetResponseAsync(); 
       Stream stream2 = wresp.GetResponseStream(); 
       StreamReader reader2 = new StreamReader(stream2); 
       //log.Debug(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd())); 
      } 
      catch (Exception ex) { 
       //log.Error("Error uploading file", ex); 
       if (wresp != null) { 
        wresp.Close(); 
        wresp = null; 
       } 
      } 
      finally { 
       wr = null; 
      } 

      /** 
      NameValueCollection nvc = new NameValueCollection(); 
      nvc.Add("id", "TTR"); 
      nvc.Add("btn-submit-photo", "Upload"); 
      HttpUploadFile("http://your.server.com/upload",   @"C:\test\test.jpg", "file", "image/jpeg", nvc);   
      **/ 
     } 

     public async Task<String> ExecutePostRequest(Uri url, Dictionary<string, string> postData, FileInfo fileToUpload, string fileMimeType, string fileFormKey) { 
      HttpWebRequest request = WebRequest.Create(url.AbsoluteUri) as HttpWebRequest; 
      request.Method = "POST"; 
      request.KeepAlive = true; 

      String boundary = Utility.CreateFormDataBoundary(); 
      request.ContentType = "multipart/form-data; boundary=" + boundary; 

      Stream requestStream = await request.GetRequestStreamAsync(); 
      postData.WriteMultipartFormData(requestStream, boundary); 

      if (fileToUpload != null) { 
       //TODO: Need async here... 
       fileToUpload.WriteMultipartFormData(requestStream, boundary, fileMimeType, fileFormKey); 
      } 

      byte[] endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--"); 

      await requestStream.WriteAsync(endBytes, 0, endBytes.Length); 
      requestStream.Close(); 

      using (WebResponse response = await request.GetResponseAsync()) { 
       using (StreamReader reader = new StreamReader(response.GetResponseStream())) { 
        return await reader.ReadToEndAsync(); 
       } 
      } 
     } 

    } 

} 

注:有三种方法,即是对文件上传结束。我仍然需要计算出来,然后在做之前,我需要了解取消和进度报告。

相关问题Async CTP for a PostSubmitter

任何帮助将非常感激。

回答

1

您支持取消进度和取消IProgress<T>CancellationToken参数。

如需取消,请致电CancellationToken.ThrowIfCancellationRequested定期检查是否已要求取消。有关更多信息,请参见Cancellation on MSDN

为了取得进展,您需要先决定什么样的“进步”是有意义的。例如,如果“进度”只是传送的字节数,那么你可以使用IProgress<int>。一旦你决定了你的进度类型,然后致电IProgress<T>.Report报告进度。有两件事情需要注意的为IProgress<T>

  1. IProgress<T>参数可以是null
  2. IProgress<T>.Report异步运行。这意味着您必须:A)在IProgress<T>中使用T的值类型; B)对每个T对象执行深度复制,传递给IProgress<T>.Report;或C)每次拨打IProgress<T>.Report时创建一个新的T对象。
+0

伟大的联系。我知道我必须在这里效仿。也许不是简单的POST或GET,而是实际的文件上传或下载。但是,CancellationTokenSource对象的标记将被提供给异步函数。我通过Async CTP使用的*异步函数支持参数 – DoomerDGR8

+0

您可以通过调用'ThrowIfCancellationRequested'来自己观察'CancellationToken'。 –