简体   繁体   English

C#创建具有偏移量的FileStream

[英]C# Create a FileStream with an offset

Question

Is there a way create a FileStream in C# with an offset? 有没有一种方法可以在C#中使用偏移量创建FileStream For example, if I open SomeFile.bin at offset 100, Stream.Position would equal 0, but reads & writes would be offset by 100. 例如,如果我在偏移量100处打开SomeFile.bin ,则Stream.Position等于0,但读写偏移量为100。

Background 背景

I'm working on a hybrid file format for my company that combines machine-readable binary data with a modern PC-readable OPC file (essentially a ZIP file created using System.IO.Packaging ). 我正在为我的公司开发一种混合文件格式,该格式将机器可读的二进制数据与现代的PC可读的OPC文件(实际上是使用System.IO.Packaging创建的ZIP文件)结合在一起。 For performance reasons, the binary data must appear at the top of the file. 出于性能原因,二进制数据必须出现在文件的顶部。 I understand that ZIP files will allow this (eg self-extracting archives), but unfortunately the internal ZipIOCentralDirectoryBlock class is hard-coded to reject ZIP files where the first file header doesn't appear at offset 0 . 我知道ZIP文件将允许这样做(例如,自解压档案),但是不幸的是,内部ZipIOCentralDirectoryBlock类被硬编码为拒绝第一个文件头不在偏移量0出现的ZIP文件 In the interest of avoiding temporary files (since the files can be as large as 3.99GB), I would like to fool ZipPackage into thinking it's dealing with the start of a file, when in reality it's reading & writing at an offset. 为了避免使用临时文件(因为文件可以达到3.99GB),我想让ZipPackage认为它正在处理文件的开头,而实际上却是在偏移地读写。

Sure. 当然。 This is a perfect case for the Decorator Pattern : 这是Decorator模式的完美案例:

Basically you create a class that 基本上,您创建一个

  • inherits from Stream (the abstract class you are decorating) 继承自Stream (您正在装饰的抽象类)
  • has a constructor that accepts a single instance of that base class 有一个接受该基类的单个实例的构造函数

Then you override all the methods and properties, passing the call through to the decorated instance. 然后,您将覆盖所有方法和属性,并将调用传递到经过修饰的实例。 If the method or property has knowledge of the stream's position or length, you apply the appropriate adjustment as needed. 如果方法或属性知道流的位置或长度,则可以根据需要应用适当的调整。

Edited to note: It looks like you need to decorate the abstract stream as shown below (no way to create a filestream instance without actually opening a file). 编辑注意: 看起来您需要装饰抽象流,如下所示(在没有实际打开文件的情况下无法创建文件流实例)。

Here's a [truncated] example of the decorator itself: 这是装饰器本身的一个[截断]示例:

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

}

Usage is pretty straightforward, something along these lines: 用法非常简单,遵循以下原则:

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 ) ;

  }

}

Easy! 简单! (except for all the boilerplate involved) (所有涉及的样板除外)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM