file io-C#创建一个带有偏移量的FileStream

本文关键字:偏移量 FileStream 一个 创建 io-C# file | 更新日期: 2023-09-27 18:00:11

问题

有没有办法在C#中创建一个带有偏移量的FileStream?例如,如果我在偏移量100处打开SomeFile.bin,则Stream.Position将等于0,但读取&写入将偏移100。

背景

我正在为我的公司开发一种混合文件格式,将机器可读的二进制数据与现代PC可读的OPC文件(本质上是使用System.IO.Packaging创建的ZIP文件)相结合。出于性能原因,二进制数据必须出现在文件的顶部。我知道ZIP文件会允许这样做(例如,自解压归档),但不幸的是,内部ZipIOCentralDirectoryBlock类被硬编码为拒绝第一个文件头没有出现在偏移量0处的ZIP文件。为了避免临时文件(因为文件可以大到3.99GB),我想欺骗ZipPackage,让它认为它处理的是文件的开头,而实际上它正在读取&以偏移量书写。

file io-C#创建一个带有偏移量的FileStream

当然。这是装饰图案:的完美案例

  • http://www.oodesign.com/decorator-pattern.html
  • https://en.wikipedia.org/wiki/Decorator_pattern

基本上,你创建了一个

  • 继承自Stream(您正在装饰的抽象类)
  • 具有接受该基类的单个实例的构造函数

然后覆盖所有方法和属性,将调用传递到装饰实例。如果方法或属性知道流的位置或长度,则可以根据需要应用适当的调整。

编辑注意: 看起来您需要装饰如下所示的抽象流(如果不实际打开文件,就无法创建文件流实例)

下面是装饰器本身的一个[截断]示例:

class OffsetStreamDecorator : Stream
{
  private readonly Stream instance ;
  private readonly long       offset   ;
  public static Stream Decorate( Stream instance )
  {
    if ( instance == null ) throw new ArgumentNullException("instance") ;
    FileStream decorator= new OffsetStreamDecorator( instance ) ;
    return decorator;
  }
  private OffsetStreamDecorator( FileStream instance )
  {
    this.instance = instance ;
    this.offset   = instance.Position ;
  }
  #region override methods and properties pertaining to the file position/length to transform the file positon using the instance's offset
  public override long Length
  {
    get { return instance.Length - offset ; }
  }
  public override void SetLength( long value )
  {
    instance.SetLength( value + offset );
  }
  public override long Position
  {
    get { return instance.Position - this.offset         ; }
    set {        instance.Position = value + this.offset ; }
  }
  // etc.
  #endregion
  #region override all other methods and properties as simple pass-through calls to the decorated instance.
  public override IAsyncResult BeginRead( byte[] array , int offset , int numBytes , AsyncCallback userCallback , object stateObject )
  {
    return instance.BeginRead( array , offset , numBytes , userCallback , stateObject );
  }
  public override IAsyncResult BeginWrite( byte[] array , int offset , int numBytes , AsyncCallback userCallback , object stateObject )
  {
    return instance.BeginWrite( array , offset , numBytes , userCallback , stateObject );
  }
  // etc.
  #endregion
}

用法非常简单,大致如下:

using ( Stream baseStream = new FileStream( @"c:'foo'bar'somefile.dat" , FileMode.Open , FileAccess.Read , FileShare.Read ) )
{
  // establish your offset
  baseStream.Seek( 100 , SeekOrigin.Begin ) ;
  using ( Stream decoratedStream = OffsetStreamDecorator.Decorate( baseStream ) )
  {
    // now you have a stream that reports a false position and length
    // which you should be able to use anywhere a Stream is accepted.
    PassDecoratedStreamToSomethingExpectingAVanillaStream( decoratedStream ) ;
  }
}

轻松!(除了所有涉及的样板)