WCF 传送二进制流数据 stream

from: http://blog.csdn.net/zengjibing/archive/2009/01/17/3813862.aspx

 

WCF 支持传送二进制流数据,但有一定的限制。

只有 BasicHttpBinding、WebHttpBinding、NetTcpBinding 和 NetNamedPipeBinding 支持传送流数据。
流数据类型必须是可序列化的 Stream 或 MemoryStream。
传递时消息体(Message Body)中不能包含其他数据。


我们先看看下面的例子。

注意将 Binding.TransferMode 设置为 TransferMode.Streamed,我们还可以修改 Binding.MaxReceivedMessageSize 来调整消息大小(默认是64KB)。

[ServiceContract]
public interface IFileService
{
  [OperationContract]
  void Upload(Stream stream);
}

public class FileService : IFileService, IDisposable
{
  public void Upload(Stream stream)
  {
    FileStream file = new FileStream("test.dll", FileMode.Create);
    try
    {
      BinaryWriter writer = new BinaryWriter(file);
      BinaryReader reader = new BinaryReader(stream);

      byte[] buffer;
      do
      {
        buffer = reader.ReadBytes(1024);
        writer.Write(buffer);
      }
      while (buffer.Length > 0);
    }
    finally
    {
      file.Close();
      stream.Close();
    }
  }

  public void Dispose()
  {
    Console.WriteLine("Dispose...");
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(FileService),
        new Uri("http://localhost:8080/FileService"));

      BasicHttpBinding binding = new BasicHttpBinding();
      binding.TransferMode = TransferMode.Streamed;

      host.AddServiceEndpoint(typeof(IFileService), binding, "");

      host.Open();
    });

    BasicHttpBinding binding2 = new BasicHttpBinding();
    binding2.TransferMode = TransferMode.Streamed;

    IFileService channel = ChannelFactory<IFileService>.CreateChannel(binding2,
      new EndpointAddress("http://localhost:8080/FileService"));
     
    using (channel as IDisposable)
    {
      FileStream stream = new FileStream("MyLibrary2.dll", FileMode.Open);
      channel.Test(stream);
      stream.Close();
    }
  }
}

一切正常。那么 "传递时消息体(Memory Body)中不能包含其他数据" 是什么意思?我们修改一下上面的契约,除了传递文件流外,我们还希望传递文件名。

[ServiceContract]
public interface IFileService
{
  [OperationContract]
  void Upload(string filename, Stream stream);
}

// ... 其他代码暂略 ...

当你修改完代码后,运行时你发现触发了一个 InvalidOperationException 异常。

未处理 System.InvalidOperationException
  Message="For request in operation Upload to be a stream the operation must have a single parameter whose type is Stream."
  Source="System.ServiceModel"

那么该怎么办呢?DataContract 肯定不行。  没错!你应该记得 MessageContract,将 filename 放到 MessageHeader 里面就行了。

[MessageContract]
public class FileData
{
  [MessageHeader]public string filename;
  [MessageBodyMember]public Stream data;
}

[ServiceContract]
public interface IFileService
{
  [OperationContract]
  void Upload(FileData file);
}

public class FileService : IFileService, IDisposable
{
  public void Upload(FileData file)
  {
    FileStream f = new FileStream(file.filename, FileMode.Create);
    try
    {
      BinaryWriter writer = new BinaryWriter(f);
      BinaryReader reader = new BinaryReader(file.data);

      byte[] buffer;
      do
      {
        buffer = reader.ReadBytes(1024);
        writer.Write(buffer);
      }
      while (buffer.Length > 0);
    }
    finally
    {
      f.Close();
      file.data.Close();
    }
  }

  public void Dispose()
  {
    Console.WriteLine("Dispose...");
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(FileService),
        new Uri("http://localhost:8080/FileService"));

      BasicHttpBinding binding = new BasicHttpBinding();
      binding.TransferMode = TransferMode.Streamed;

      host.AddServiceEndpoint(typeof(IFileService), binding, "");

      host.Open();
    });

    BasicHttpBinding binding2 = new BasicHttpBinding();
    binding2.TransferMode = TransferMode.Streamed;

    IFileService channel = ChannelFactory<IFileService>.CreateChannel(binding2,
      new EndpointAddress("http://localhost:8080/FileService"));
     
    using (channel as IDisposable)
    {
      FileData file = new FileData();
      file.filename = "test2.dll";
      file.data = new FileStream("MyLibrary2.dll", FileMode.Open);

      channel.Upload(file);

      file.data.Close();
    }
  }
}

问题解决了。上面的例子使用 BaseHttpBinding,如果使用 NetTcpBinding,相信速度要快很多。除了向服务器传送流外,也可反向返回流数据。

[ServiceContract]
public interface IFileService
{
  [OperationContract]
  void Upload(Stream stream);

  [OperationContract]
  Stream Download(string filename);
}

虽然服务器在操作结束时会自动关闭客户端 Request Stream,但个人建议还是使用 try...finnaly... 自主关闭要好一些,因为意外总是会发生的。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zengjibing/archive/2009/01/17/3813862.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值