我有一些大型数据文件,我可以使用专门为此设计的API以32kb的形式检索。 API的一种用法可以是:
LargeFileAPI lfa = new LargeFileAPI("file1.bin");
bool moredata = true;
List<byte[]> theWholeFile = new List<byte[]>();
while ( moredata )
{
byte[] arrayRead = new byte[32768];
moredata = lfa.Read(arrayRead);
theWholeFile.Add(arrayRead);
}
上面的问题是,从中读取内存的大小与大文件的大小相同(比方说100Mb)。由于我想将此作为返回结果传递给WCF服务,我宁愿使用Stream作为服务的输出。
如何从中创建Stream对象并将其作为返回参数传递给WCF服务而不占用内存中的完整文件大小?
我正在考虑创建一个继承自
的类LargeFileStreamSystem.IO.Stream
并覆盖Read方法。但我似乎无法弄清楚如何处理Stream.Read采用偏移参数和要读取的字节数这一事实,因为我提到的API需要为每次读取读取固定数量的字节。此外,我必须覆盖的所有其他方法,如Flush(),Position和其他任何方法。他们应该做些什么?我问,因为我不知道除了Stream.Read()之外的其他功能,当我从客户端(WCF服务的调用者)读取流时,WCF会调用。
此外,我需要它可序列化,以便它可以作为WCF服务的输出参数。
由于 圣战
答案 0 :(得分:2)
您可以使用api大小的一个缓冲区(即32kb)编写您的流以执行您想要的操作,并在阅读时将其回收。示例代码如下(不是它不是生产准备就绪,需要测试,但有些东西可以帮助你开始):
public class LargeFileApiStream : Stream {
private readonly LargeFileApi _api;
private bool _hasMore;
private bool _done;
private byte[] _buffer;
const int ApiBufferSize = 32768;
public LargeFileApiStream(LargeFileApi api) {
_api = api;
}
public override void Flush() {
// you can ignore that, this stream is not writable
}
public override long Seek(long offset, SeekOrigin origin) {
throw new NotSupportedException(); // not seekable, only read from beginning to end
}
public override void SetLength(long value) {
throw new NotSupportedException(); // not writable
}
public override void Write(byte[] buffer, int offset, int count) {
throw new NotSupportedException(); // not writable
}
public override int Read(byte[] buffer, int offset, int count) {
// if we reached end of stream before - done
if (_done)
return 0;
if (_buffer == null) {
// we are just starting, read first block
_buffer = new byte[ApiBufferSize];
_hasMore = _api.Read(_buffer);
}
var nextIndex = _position % ApiBufferSize;
int bytesRead = 0;
for (int i = 0; i < count; i++) {
if (_buffer.Length <= nextIndex) {
// ran out of current chunk - fetch next if possible
if (_hasMore) {
_hasMore = _api.Read(_buffer);
}
else {
// we are done, nothing more to read
_done = true;
break;
}
// reset next index back to 0, we are now reading next chunk
nextIndex = 0;
buffer[offset + i] = _buffer[nextIndex];
nextIndex++;
bytesRead++;
}
else {
// write byte to output buffer
buffer[offset + i] = _buffer[nextIndex];
nextIndex++;
bytesRead++;
}
}
_position += bytesRead;
return bytesRead;
}
public override bool CanRead {
get { return true; }
}
public override bool CanSeek {
get { return false; }
}
public override bool CanWrite {
get { return false; }
}
public override long Length {
get { throw new NotSupportedException(); }
}
private long _position;
public override long Position
{
get { return _position; }
set { throw new NotSupportedException(); } // not seekable
}
}
答案 1 :(得分:1)
只需将您的数据存储在临时文件中,如下所示:
// create temporary stream
var stream = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
try
{
// write all data to temporary stream
while (moredata)
{
byte[] arrayRead = new byte[32768];
moredata = lfa.Read(arrayRead);
stream.Write(arrayRead, 0, arrayRead.Length);
}
stream.Flush();
stream.Position = 0; // Reset position so stream will be read from beginning
}
catch
{
stream.Close(); // close stream to delete temporary file if error occured
}
临时文件流保存从LargeFileApi接收的数据。由于数据实际存储在文件中,因此不会耗尽内存。
由于传递给构造函数的FileOptions.DeleteOnClose
选项,流将关闭后将删除临时文件。因此,如果出现问题或完成阅读,您可以关闭流。
答案 2 :(得分:0)
您可以执行以下操作:
使用netTcpBinding创建WCF服务。该服务可以返回一个应用了MessageContract属性的对象
[MessageContract] 公共类LargeStream {
[MessageHeader]
public int Section { get; set; }
[MessageBodyMember]
public Stream Data { get; set; }
}
如果您想添加其他元数据,请使用MessageHeader属性修饰它们。
在客户端,Web应用程序可以使用该服务并发出两个请求。