WCF默认是缓存传输模式,但是这样会占用服务器宝贵的内存资源,连接数越多,占用的内存就越多,当传输大数据特别是大文件的时候,内存会消耗光。有人给出过WCF下大数据传输方案,不过那还是在缓存模式下的,而且不方便管理,无法实际使用。
显然,使用流传输模式可以解决上述问题。不过流也有些不便之处,首先是不支持会话,它只能在PerCall的模式下工作,用户验证信息无法通过会话的方式存于服务器端了,不过这可以使用消息头来发送验证消息;其次是不支持使用证书,不过关于这点,我的理解是,流传输也不需要证书加密,本身传输的就是流,不是完整的内容,所以就算被拦截了也无法解读。
默认的流传输有个缺点,无法指定流的偏移和长度来传输,它永远是传输全部流,当服务器端读取到流的结束位置时,传输结束。不过我们只要改造下要传输的流,就可以实现我们所要的效果了,下面展示文件流传输中,指定传输的起始位置和传输的字节数。客户端的调用就不列出来了,用途很多,比如你可以使用它来实现断点续传,传输过程中记录传输量,一旦流传输中断,则下次重新传输时只要从记录的传输量的位置开始重新传输。也可以作为多线程传输的方式,人为的将文件分块后开启多个客户端连接来传输文件。
关键代码如下:
[ServiceContract]
public interface IServer : IDisposable
{
[OperationContract]
Stream DownloadFileStream(string path, long offset, long count);
}
public interface IServer : IDisposable
{
[OperationContract]
Stream DownloadFileStream(string path, long offset, long count);
}
自定义文件流
public class CusFileStream : FileStream
{
long _count;
long _lastPosition;
public CusStream(IntPtr handle, FileAccess access)
: base(handle, access) { }
public CusStream(string path, FileMode mode, FileAccess access, FileShare share)
: base(path, mode, access) { }
public CusStream(string path, FileMode mode, FileAccess access, FileShare share, long offset, long count)
: base(path, mode, access)
{
base.Position = offset;
_count = count;
_lastPosition = offset + count;
}
public CusStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
: base(path, mode, access, share, bufferSize) { }
public override int Read(byte[] array, int offset, int count)
{
if (this._count > 0 && Position + count > this._lastPosition)
return base.Read(array, offset, (int)(this._lastPosition - Position));
else
return base.Read(array, offset, count);
}
public override int ReadByte()
{
if (this._count > 0 && Position >= this._lastPosition)
return -1;
else
return base.ReadByte();
}
}
{
long _count;
long _lastPosition;
public CusStream(IntPtr handle, FileAccess access)
: base(handle, access) { }
public CusStream(string path, FileMode mode, FileAccess access, FileShare share)
: base(path, mode, access) { }
public CusStream(string path, FileMode mode, FileAccess access, FileShare share, long offset, long count)
: base(path, mode, access)
{
base.Position = offset;
_count = count;
_lastPosition = offset + count;
}
public CusStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
: base(path, mode, access, share, bufferSize) { }
public override int Read(byte[] array, int offset, int count)
{
if (this._count > 0 && Position + count > this._lastPosition)
return base.Read(array, offset, (int)(this._lastPosition - Position));
else
return base.Read(array, offset, count);
}
public override int ReadByte()
{
if (this._count > 0 && Position >= this._lastPosition)
return -1;
else
return base.ReadByte();
}
}
服务类
[ServiceBehavior]
public class Server : IServer
{
public Stream DownloadFileStream(string path, long offset, long count)
{
CusStream fs = new CusStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, offset, count);
return fs;
}
}
public class Server : IServer
{
public Stream DownloadFileStream(string path, long offset, long count)
{
CusStream fs = new CusStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, offset, count);
return fs;
}
}