WCF4.0 –- RESTful WCF Services (3) (Raw Stream) 【转】

http://blog.csdn.net/fangxing80/article/details/6261431

前面2篇blog,主要在介绍REST WCF如何通过json/xml 和客户端完成交互。 这篇文章将对REST WCF的原生(RAW)流传输进行一次实例分析。
OK,还是利用 WCF REST Service Application 的工程模板,创建我们的REST WCF工程。
因为WCF REST服务不需要公开metadata, 所以 OperationContract 的定义也省略了。直接定义[ServiceContract] 如果修改模板中Service名,Global.asax中的RegisterRoutes方法也必须相应的修改。 下面是服务的实现:

  1. [ServiceContract]  
  2. [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  3. [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, Namespace="")]  
  4. public class ImageService  
  5. {  
  6.     [WebGet(UriTemplate = "{image}")]  
  7.     public Stream GetImage(string image)  
  8.     {  
  9.         var imageType = Path.GetExtension(image).TrimStart('.');  
  10.         WebOperationContext.Current.OutgoingResponse.ContentType = "image/" + imageType;  
  11.         var dir = System.Web.HttpContext.Current.Server.MapPath("~/Images");  
  12.         var file = Path.Combine(dir, image);  
  13.         return File.OpenRead(file);  
  14.     }  
  15.   
  16.     [WebInvoke(UriTemplate = "Add/{image}", Method="POST")]  
  17.     public void AddImage(Stream stream, string image)  
  18.     {  
  19.         var dir = System.Web.HttpContext.Current.Server.MapPath("~/Images");  
  20.         var file = Path.Combine(dir, image);  
  21.         var bitmap = Bitmap.FromStream(stream);  
  22.         bitmap.Save(file);  
  23.     }  
  24.   
  25.     [WebGet(UriTemplate = "ListAll", ResponseFormat=WebMessageFormat.Xml)]  
  26.     public string[] GetAllImageNames()  
  27.     {  
  28.         var dir = System.Web.HttpContext.Current.Server.MapPath("~/Images");  
  29.         var files = Directory.GetFiles(dir);  
  30.         var images = new List<string>();  
  31.         foreach (var file in files)  
  32.         {  
  33.             var ext = Path.GetExtension(file);  
  34.             if (ext == ".jpg" || ext == ".gif")  
  35.                 images.Add(file);  
  36.         }  
  37.         for (int i = 0; i < images.Count; i++)  
  38.             images[i] = Path.GetFileName(images[i]);  
  39.         return images.ToArray();  
  40.     }  
  41. }  


- GetImage 用于返回图片 Stream, - AddImage 用于接受图片 Stream 并保存, - GetAllImageNames 返回所有图片名。
在Http协议中,通过content-type控制客户端的解析行为。当content-type是"image/jpg"或者是"image/gif"的时候, 浏览器就能够直接显示图片了。因此,在代码里有这样的设定: WebOperationContext.Current.OutgoingResponse.ContentType = "image/" + imageType; 还可以通过 OutgoingResponse.Header 控制缓存。另外,这个REST WCF是建立在一个Web Application中, 我们还可以利用  System.Web.HttpContext.Current.Server.MapPath 方法,获得物理路径。
我们知道一般的WCF服务中,可以实现Streamed传输。在那种场合下,服务契约必须的参数或者返回值必须是Stream,且不能有别的类型的参数(要传递别的参数可以通过MessageHeader)。但是在上面的示例代码中,我定义的 AddImage 就有两个参数: 一个是Stream,还有一个是string。这个string参数其实是通过url传递的: UriTemplate = "Add/{image}",且这两个参数没有顺序要求。 如果没有写"{image}"会抛出下面的异常(因为Request的Body已经被定义为Stream,图片名只能通过Url传过来。) Operation 'AddImage' of contract 'ImageService' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.
运行服务,直接在浏览器里输入服务URL:http://localhost:3577/ImageService/GIF002.gif 我们就可以看到结果了。 另外,利用REST WCF返回原生流的特性,我们完全可以取代以前在 ASP.NET 中专门生成验证码或者图片读取步骤的页面类, 是不是很方便?

为了体现 REST WCF 的便利性, 我设计了一个 WPF 的客户端: 客户端代码:

  1. public partial class MainWindow : Window  
  2. {  
  3.     public MainWindow()  
  4.     {  
  5.         InitializeComponent();  
  6.     }  
  7.   
  8.     private void Window_Loaded(object sender, RoutedEventArgs e)  
  9.     {  
  10.         op.RestoreDirectory = true;  
  11.         op.Filter = "Jpeg Files(*.jpg)|*.jpg|Gif Files(*.gif)|*.gif";  
  12.   
  13.         BindData();  
  14.     }  
  15.   
  16.     private void BindData()  
  17.     {  
  18.         var url = "http://localhost:3577/ImageService/ListAll";  
  19.         var client = new HttpClient();  
  20.         var stream = client.Get(url).Content.ReadAsStream();  
  21.         var dataSer = new DataContractSerializer(typeof(string[]));  
  22.         var obj = (string[])dataSer.ReadObject(stream);  
  23.         this.listBox1.DataContext = obj;  
  24.     }  
  25.   
  26.     private Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog();  
  27.   
  28.     private void button1_Click(object sender, RoutedEventArgs e)  
  29.     {  
  30.         var ret = op.ShowDialog();  
  31.         if (!ret.HasValue || !ret.Value || !op.CheckFileExists) return;  
  32.         var file = op.FileName;  
  33.         var name = System.IO.Path.GetFileName(file);  
  34.         var url = "http://localhost:3577/ImageService/Add/" + name;  
  35.         var client = new HttpClient();  
  36.         var content = HttpContent.Create(File.OpenRead(file));  
  37.         var resp = client.Post(url, content);  
  38.         resp.EnsureStatusIsSuccessful();  
  39.   
  40.         BindData();  
  41.     }  
  42.   
  43.     private void button2_Click(object sender, RoutedEventArgs e)  
  44.     {  
  45.         this.Close();  
  46.     }  
  47. }  
  48.   
  49. [ValueConversion(typeof(string), typeof(string))]  
  50. public class UriConverter : IValueConverter  
  51. {  
  52.     public object Convert(object value, Type targetType,   
  53.         object parameter, System.Globalization.CultureInfo culture)  
  54.     {  
  55.         if (value == null) return null;  
  56.         string sourceValue = value.ToString();  
  57.         return "http://localhost:3577/ImageService/" + sourceValue;  
  58.     }  
  59.   
  60.     public object ConvertBack(object value, Type targetType,  
  61.         object parameter, System.Globalization.CultureInfo culture)  
  62.     {  
  63.         return value;  
  64.     }  
  65. }  

实现的注意点: 1) 获得所有图片名,服务端返回的是string[]的序列化的xml,用DataContractSerializer反序列化。     var dataSer = new DataContractSerializer(typeof(string[])); 2) WPF中的OpenFileDialog在Microsoft.Win32命名空间下。(必须引用WindowsBase.dll) 3) 利用 Microsoft.Http.HttpClient 上传图片, 实例化 HttpContent, 通过client.Post上传     var url = "http://localhost:3577/ImageService/Add/" + name;     var client = new HttpClient();     var content = HttpContent.Create(File.OpenRead(file));     var resp = client.Post(url, content);     resp.EnsureStatusIsSuccessful(); 4) 定义了一个Converter, 将 ListBox 绑定到 Image 控件的 Source     Source="{Binding ElementName=listBox1, Path=SelectedItem, Converter={StaticResource uriConv}}"

 

源代码下载:REST WCF Raw Stream 示例代码

posted @ 2016-02-02 16:28  J.Y  阅读(367)  评论(0编辑  收藏  举报