我们知道,在实现WCF传送二进制流数据这一操作过程中,会有一些限制因素。我们在实际应用中要特别注意这一点。今天我们就会针对这方面的问题做一个详细的介绍,希望对大家有所帮助。#t#
只有 BasicHttpBinding、WebHttpBinding、NetTcpBinding 和 NetNamedPipeBinding 支持传送流数据。
流数据类型必须是可序列化的 Stream 或 MemoryStream。
传递时消息体(Message Body)中不能包含其他数据。
我们先看看下面的WCF传送二进制流数据例子。
注意将 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();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
一切正常。那么 "传递时消息体(Memory Body)中不能包含其他数据" 是什么意思?我们修改一下上面的契约,除了传递文件流外,我们还希望传递文件名。
[ServiceContract]
public interface IFileService
{
[OperationContract]
void Upload(string filename, Stream stream);
}
// ... 其他代码暂略 ...
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
当你修改完WCF传送二进制流数据的代码后,运行时你发现触发了一个 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();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
问题解决了。上面的例子使用 BaseHttpBinding,如果使用 NetTcpBinding,相信速度要快很多。除了向服务器传送流外,也可反向返回流数据。
[ServiceContract]
public interface IFileService
{
[OperationContract]
void Upload(Stream stream);
[OperationContract]
Stream Download(string filename);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
虽然服务器在操作结束时会自动关闭客户端 Request Stream,但个人建议还是使用 try...finnaly... 自主关闭要好一些,因为意外总是会发生的。
WCF传送二进制流数据的全部操作方法就为大家介绍到这里。