我有一个包含许多数据片段的流。我想在另一个流中仅暴露其中的一部分数据。我想要提取的数据片段通常超过100MB。由于我已经有了包含这些数据的流,将数据复制到另一个流中并返回似乎是一种浪费。我要找的是一种方法,可以引用第一个流中的数据,同时控制第二个流可以引用多少数据。这可能吗?
我有一个包含许多数据片段的流。我想在另一个流中仅暴露其中的一部分数据。我想要提取的数据片段通常超过100MB。由于我已经有了包含这些数据的流,将数据复制到另一个流中并返回似乎是一种浪费。我要找的是一种方法,可以引用第一个流中的数据,同时控制第二个流可以引用多少数据。这可能吗?
这方面的一个很好的实现是由Mark Gravell完成的,详细内容请见。他发布的代码如下:
using System.IO;
using System;
static class Program
{
// shows that we can read a subset of an existing stream...
static void Main()
{
byte[] buffer = new byte[255];
for (byte i = 0; i < 255; i++)
{
buffer[i] = i;
}
using(MemoryStream ms = new MemoryStream(buffer))
using (SubStream ss = new SubStream(ms, 10, 200))
{
const int BUFFER_SIZE = 17; // why not...
byte[] working = new byte[BUFFER_SIZE];
int read;
while ((read = ss.Read(working, 0, BUFFER_SIZE)) > 0)
{
for (int i = 0; i < read; i++)
{
Console.WriteLine(working[i]);
}
}
}
}
}
class SubStream : Stream
{
private Stream baseStream;
private readonly long length;
private long position;
public SubStream(Stream baseStream, long offset, long length)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
if (!baseStream.CanRead) throw new ArgumentException("can't read base stream");
if (offset < 0) throw new ArgumentOutOfRangeException("offset");
this.baseStream = baseStream;
this.length = length;
if (baseStream.CanSeek)
{
baseStream.Seek(offset, SeekOrigin.Current);
}
else
{ // read it manually...
const int BUFFER_SIZE = 512;
byte[] buffer = new byte[BUFFER_SIZE];
while (offset > 0)
{
int read = baseStream.Read(buffer, 0, offset < BUFFER_SIZE ? (int) offset : BUFFER_SIZE);
offset -= read;
}
}
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
long remaining = length - position;
if (remaining <= 0) return 0;
if (remaining < count) count = (int) remaining;
int read = baseStream.Read(buffer, offset, count);
position += read;
return read;
}
private void CheckDisposed()
{
if (baseStream == null) throw new ObjectDisposedException(GetType().Name);
}
public override long Length
{
get { CheckDisposed(); return length; }
}
public override bool CanRead
{
get { CheckDisposed(); return true; }
}
public override bool CanWrite
{
get { CheckDisposed(); return false; }
}
public override bool CanSeek
{
get { CheckDisposed(); return false; }
}
public override long Position
{
get {
CheckDisposed();
return position;
}
set { throw new NotSupportedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Flush()
{
CheckDisposed(); baseStream.Flush();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
if (baseStream != null)
{
try { baseStream.Dispose(); }
catch { }
baseStream = null;
}
}
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
看起来StreamMuxer项目是为了类似的目的而创建的。
我还需要一个子流,以便我可以在另一个文件内部处理ZIP归档文件,但另一个答案没有实现寻址,因此这里提供一个带有寻址功能的答案;请注意,当被处置时,它不会处理原始流:
public class SubStream : Stream
{
Stream Vector;
long Offset, _Length, _Position = 0;
public SubStream(Stream vector, long offset, long length)
{
if (length < 1) throw new ArgumentException("Length must be greater than zero.");
this.Vector = vector;
this.Offset = offset;
this._Length = length;
vector.Seek(offset, SeekOrigin.Begin);
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
long remaining = _Length - _Position;
if (remaining <= 0) return 0;
if (remaining < count) count = (int)remaining;
int read = Vector.Read(buffer, offset, count);
_Position += read;
return read;
}
private void CheckDisposed()
{
if (Vector == null) throw new ObjectDisposedException(GetType().Name);
}
public override long Seek(long offset, SeekOrigin origin)
{
long pos = _Position;
if (origin == SeekOrigin.Begin)
pos = offset;
else if (origin == SeekOrigin.End)
pos = _Length + offset;
else if (origin == SeekOrigin.Current)
pos += offset;
if (pos < 0) pos = 0;
else if (pos >= _Length) pos = _Length - 1;
_Position = Vector.Seek(this.Offset + pos, SeekOrigin.Begin) - this.Offset;
return pos;
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => _Length;
public override long Position { get => _Position; set { _Position = this.Seek(value, SeekOrigin.Begin); } }
public override void Flush()
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
你到底害怕复制什么?我怀疑你没有什么超级高性能的东西,可以在流上实时解析并使用内存流,直到你发现需要其他东西。