[
  {
    "Id": "432100",
    "ThreadId": "209615",
    "Html": "<p>The following code throws an exception when I write some data in front of the zip file. It works fine for start=0.</p>\r\n<p>What am I doing wrong?</p>\r\n<p>\r\n<div style=\"color:black;background-color:white\">\r\n<pre><span style=\"color:blue\">using</span> System;\r\n<span style=\"color:blue\">using</span> System.Collections.Generic;\r\n<span style=\"color:blue\">using</span> System.Linq;\r\n<span style=\"color:blue\">using</span> System.Text;\r\n<span style=\"color:blue\">using</span> System.IO;\r\n<span style=\"color:blue\">using</span> Ionic.Zip;\r\n<span style=\"color:blue\">using</span> System.Diagnostics;\r\n\r\n<span style=\"color:blue\">namespace</span> ZipTest\r\n{\r\n\t<span style=\"color:blue\">class</span> Program\r\n\t{\r\n\t\t<span style=\"color:blue\">const</span> <span style=\"color:blue\">int</span> start = 64;\r\n\t\t<span style=\"color:blue\">static</span> <span style=\"color:blue\">long</span> size;\r\n\t\t<span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Save()\r\n\t\t{\r\n\t\t\t<span style=\"color:blue\">using</span> (FileStream f = File.Create(<span style=\"color:#a31515\">&quot;test.zip&quot;</span>))\r\n\t\t\t{\r\n\t\t\t\tf.Write(<span style=\"color:blue\">new</span> <span style=\"color:blue\">byte</span>[start], 0, start);\r\n\t\t\t\tZipFile zip = <span style=\"color:blue\">new</span> ZipFile(Encoding.UTF8);\r\n\t\t\t\tzip.AddEntry(<span style=\"color:#a31515\">&quot;a.txt&quot;</span>, <span style=\"color:#a31515\">&quot;abc&quot;</span>);\r\n\t\t\t\tzip.Save(f);\r\n\t\t\t\tsize = f.Length;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t<span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Load()\r\n\t\t{\r\n\t\t\t<span style=\"color:blue\">using</span> (FileStream f = File.OpenRead(<span style=\"color:#a31515\">&quot;test.zip&quot;</span>))\r\n\t\t\t{\r\n\t\t\t\tf.Seek(start, SeekOrigin.Begin);\r\n\t\t\t\tZipFile zip = ZipFile.Read(f, Encoding.UTF8);<span style=\"color:green\">//throws</span>\r\n\t\t\t\t<span style=\"color:green\">/*An unhandled exception of type 'Ionic.Zip.BadReadException' occurred in Ionic.Zip.Reduced.dll\r\n\t\t\t\tAdditional information:   ZipEntry::ReadHeader(): Bad signature (0xCADDAD9C) at position  0x00000074*/</span>\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t<span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Main(<span style=\"color:blue\">string</span>[] args)\r\n\t\t{\r\n\t\t\tSave();\r\n\t\t\tLoad();\r\n\t\t}\r\n\t}\r\n}\r\n\r\n</pre>\r\n</div>\r\n</p>",
    "PostedDate": "2010-04-16T14:56:13.547-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432592",
    "ThreadId": "209615",
    "Html": "<p><strong>Edit: Sorry, didn't see that you were also starting to write at that position - my original anwer was wrong. Appears it's either how the stream is written or parsed.</strong></p>\r\n<p>&nbsp;</p>\r\n<p>&nbsp;</p>",
    "PostedDate": "2010-04-19T00:40:42.947-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432782",
    "ThreadId": "209615",
    "Html": "<p>Hi MasterOfChaos,</p>\r\n<p>I think the problem is to do with the way ZipFile calculates offsets to data blocks within the zip stream. Some of it is using absolute file positions including the padding, and&nbsp;other parts&nbsp;are relative positions from the start of the zip&nbsp;data - they don't seem to quite marry up between the read and write operations.</p>\r\n<p>You could work around this by zipping the data to a temporary file (or memory)&nbsp;and then embedding the&nbsp;output from that&nbsp;into your file stream. This will force all offsets to be relative to the start of the zip data and you can do the reverse later&nbsp;to unzip them again.</p>\r\n<p>Be aware that i<span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span>f <span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span>you <span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span>do <span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span>this <span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span><span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span><span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span><span id=\"_mce_start\" style=\"display:none;line-height:0\">﻿</span><span id=\"_mce_end\" style=\"display:none;line-height:0\">﻿</span><span id=\"_mce_end\" style=\"display:none;line-height:0\">﻿</span>you'll lose the ability to extract data directly from the&nbsp;composite file&nbsp;using WinZip (which currently works with your test.zip), so it depends on what you're trying to achieve. I've put some sample code below - if you're creating large zip files then memory may not be the best place to store the temporary zip data.</p>\r\n<p>Cheers,</p>\r\n<p>Mike</p>\r\n<p><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#2b91af;font-family:Consolas\"><span style=\"font-size:x-small;color:#2b91af;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#808080;font-family:Consolas\"><span style=\"font-size:x-small;color:#808080;font-family:Consolas\"><span style=\"font-size:x-small;color:#808080;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;color:#0000ff;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;color:#008000;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\"><span style=\"font-size:x-small;font-family:Consolas\">&nbsp;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>\r\n<div style=\"color:black;background-color:white\">\r\n<pre><span style=\"color:blue\">namespace</span> Discussion_209615\r\n{\r\n    <span style=\"color:blue\">static</span> <span style=\"color:blue\">class</span> Program\r\n    {\r\n\r\n        <span style=\"color:blue\">const</span> <span style=\"color:blue\">int</span> start = 64;\r\n        <span style=\"color:blue\">const</span> <span style=\"color:blue\">string</span> fileName = <span style=\"color:#a31515\">&quot;test-pad.zip&quot;</span>;\r\n\r\n        <span style=\"color:blue\">static</span> <span style=\"color:blue\">long</span> size;\r\n        \r\n        <span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Save()\r\n        {\r\n            <span style=\"color:blue\">using</span> (FileStream f = File.Create(fileName))\r\n            {\r\n                f.Write(<span style=\"color:blue\">new</span> <span style=\"color:blue\">byte</span>[start], 0, start);\r\n                <span style=\"color:blue\">using</span> (MemoryStream m = <span style=\"color:blue\">new</span> MemoryStream())\r\n                {\r\n                    <span style=\"color:green\">// zip into a memory stream</span>\r\n                    ZipFile zip = <span style=\"color:blue\">new</span> ZipFile(Encoding.UTF8);\r\n                    zip.AddEntry(<span style=\"color:#a31515\">&quot;a.txt&quot;</span>, <span style=\"color:#a31515\">&quot;abc&quot;</span>);\r\n                    zip.Save(m);\r\n                    m.Flush();\r\n                    m.Seek(0, SeekOrigin.Begin);\r\n                    <span style=\"color:green\">// read the data to a byte array</span>\r\n                    <span style=\"color:blue\">byte</span>[] data = <span style=\"color:blue\">new</span> <span style=\"color:blue\">byte</span>[m.Length];\r\n                    <span style=\"color:blue\">if</span> (m.Read(data, 0, data.Length) != data.Length) <span style=\"color:blue\">throw</span> <span style=\"color:blue\">new</span> System.InvalidOperationException();\r\n                    <span style=\"color:green\">// write to the file</span>\r\n                    f.Write(data, 0, data.Length);\r\n                }\r\n                size = f.Length;\r\n            }\r\n        }\r\n\r\n        <span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Load()\r\n        {\r\n            <span style=\"color:blue\">using</span> (FileStream f = File.OpenRead(fileName))\r\n            {\r\n                f.Seek(start, SeekOrigin.Begin);\r\n                <span style=\"color:green\">// read from the file</span>\r\n                <span style=\"color:blue\">byte</span>[] data = <span style=\"color:blue\">new</span> <span style=\"color:blue\">byte</span>[f.Length - f.Position];\r\n                <span style=\"color:blue\">if</span> (f.Read(data, 0, data.Length) != data.Length) <span style=\"color:blue\">throw</span> <span style=\"color:blue\">new</span> System.InvalidOperationException();\r\n                <span style=\"color:blue\">using</span> (MemoryStream m = <span style=\"color:blue\">new</span> MemoryStream())\r\n                {\r\n                    <span style=\"color:green\">// write to a memory stream</span>\r\n                    m.Write(data, 0, data.Length);\r\n                    m.Flush();\r\n                    m.Seek(0, SeekOrigin.Begin);\r\n                    <span style=\"color:green\">// read the zip data</span>\r\n                    ZipFile zip = ZipFile.Read(m, Encoding.UTF8);<span style=\"color:green\">//throws</span>\r\n                    zip.ExtractAll(<span style=\"color:#a31515\">&quot;C:\\\\temp\\\\DotNetZip\\\\debug\\\\Discussion_209615\\\\temp&quot;</span>);\r\n                }\r\n                <span style=\"color:green\">/*An unhandled exception of type 'Ionic.Zip.BadReadException' occurred in Ionic.Zip.Reduced.dll\r\n                Additional information:   ZipEntry::ReadHeader(): Bad signature (0xCADDAD9C) at position  0x00000074*/</span>\r\n            }\r\n        }\r\n\r\n        <span style=\"color:blue\">static</span> <span style=\"color:blue\">void</span> Main(<span style=\"color:blue\">string</span>[] args)\r\n        {\r\n            Save();\r\n            Load();\r\n        }\r\n\r\n    }\r\n\r\n}\r\n</pre>\r\n</div>",
    "PostedDate": "2010-04-19T10:04:43.28-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432812",
    "ThreadId": "209615",
    "Html": "As an alternative, here's a ChunkStream implementation I use to only expose a part of an original stream - this transparently masks the underlying FileStream for the ZIP implementation, so you could directly wrap your file stream within this one:\r\n\r\n<pre>\r\nusing System;\r\nusing System.IO;\r\n\r\n\r\nnamespace Vfs.Transfer.Util\r\n{\r\n  /// &lt;summary&gt;\r\n  /// A stream that provides streamed access to a given range within\r\n  /// another stream, but does not allow reading or writing outside\r\n  /// this block's boundaries.\r\n  /// &lt;/summary&gt;\r\n  public class ChunkStream : Stream\r\n  {\r\n    /// &lt;summary&gt;\r\n    /// The stream from which the data is read.\r\n    /// &lt;/summary&gt;\r\n    public Stream DecoratedStream { get; private set; }\r\n\r\n    /// &lt;summary&gt;\r\n    /// The offset (starting point) of the chunk in the\r\n    /// decorated stream.\r\n    /// &lt;/summary&gt;\r\n    public long Offset { get; private set; }\r\n\r\n    /// &lt;summary&gt;\r\n    /// The size of the chunk. This is this stream's length.\r\n    /// &lt;/summary&gt;\r\n    public long ChunkSize { get; set; }\r\n\r\n    private long position;\r\n\r\n    /// &lt;summary&gt;\r\n    /// The virtual position within the chunk. The current position\r\n    /// within the &lt;see cref=&quot;DecoratedStream&quot;/&gt; is\r\n    /// &lt;see cref=&quot;Offset&quot;/&gt; + &lt;see cref=&quot;Position&quot;/&gt;. \r\n    /// &lt;/summary&gt;\r\n    public override long Position\r\n    {\r\n      get { return position; }\r\n      set\r\n      {\r\n        if(position &lt; 0 || position &gt; ChunkSize)\r\n        {\r\n          string msg = &quot;Cannot set Position property to '{0}': Position cannot be negative or bigger than the chunk size of [{1}] bytes.&quot;;\r\n          msg = String.Format(msg, value, ChunkSize);\r\n          throw new ArgumentOutOfRangeException(&quot;value&quot;, msg);\r\n        }\r\n        position = value;\r\n\r\n        //not a problem if we set the position above the stream length\r\n        //common implementations just correct it\r\n        DecoratedStream.Position = Offset + value;\r\n      }\r\n    }\r\n\r\n    /// &lt;summary&gt;\r\n    /// Initializes a new instance of the &lt;see cref=&quot;T:System.IO.Stream&quot;/&gt; class. \r\n    /// &lt;/summary&gt;\r\n    public ChunkStream(Stream decoratedStream, long chunkSize, long offset, bool initStreamPosition)\r\n    {\r\n      DecoratedStream = decoratedStream;\r\n      ChunkSize = chunkSize;\r\n      Offset = offset;\r\n\r\n      if (initStreamPosition)\r\n      {\r\n        decoratedStream.Position = Offset;\r\n      }\r\n    }\r\n\r\n    /// &lt;summary&gt;\r\n    /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.\r\n    /// &lt;/summary&gt;\r\n    /// &lt;returns&gt;\r\n    /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.\r\n    /// &lt;/returns&gt;\r\n    /// &lt;param name=&quot;buffer&quot;&gt;An array of bytes. When this method returns, the buffer contains the specified byte array with the values between &lt;paramref name=&quot;offset&quot;/&gt; and (&lt;paramref name=&quot;offset&quot;/&gt; + &lt;paramref name=&quot;count&quot;/&gt; - 1) replaced by the bytes read from the current source. \r\n    /// &lt;/param&gt;&lt;param name=&quot;offset&quot;&gt;The zero-based byte offset in &lt;paramref name=&quot;buffer&quot;/&gt; at which to begin storing the data read from the current stream. \r\n    /// &lt;/param&gt;&lt;param name=&quot;count&quot;&gt;The maximum number of bytes to be read from the current stream. \r\n    /// &lt;/param&gt;&lt;exception cref=&quot;T:System.ArgumentException&quot;&gt;The sum of &lt;paramref name=&quot;offset&quot;/&gt; and &lt;paramref name=&quot;count&quot;/&gt; is larger than the buffer length. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ArgumentNullException&quot;&gt;&lt;paramref name=&quot;buffer&quot;/&gt; is null. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ArgumentOutOfRangeException&quot;&gt;&lt;paramref name=&quot;offset&quot;/&gt; or &lt;paramref name=&quot;count&quot;/&gt; is negative. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.IO.IOException&quot;&gt;An I/O error occurs. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.NotSupportedException&quot;&gt;The stream does not support reading. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ObjectDisposedException&quot;&gt;Methods were called after the stream was closed. \r\n    /// &lt;/exception&gt;&lt;filterpriority&gt;1&lt;/filterpriority&gt;\r\n    public override int Read(byte[] buffer, int offset, int count)\r\n    {\r\n      ValidateReadWriteParams(buffer, offset, count);\r\n\r\n      //do not read further than the chunk\r\n      int bytesToRead = (int)Math.Min(count, ChunkSize - Position);\r\n\r\n      //if we do not have anything to read, don't read at all - this closes underlying stream\r\n      //implementations!!!\r\n      if(bytesToRead == 0) return 0;\r\n      \r\n      int receivedBytes = DecoratedStream.Read(buffer, offset, bytesToRead);\r\n      \r\n      //update received bytes\r\n      position += receivedBytes;\r\n\r\n      return receivedBytes;\r\n    }\r\n\r\n\r\n    /// &lt;summary&gt;\r\n    /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.\r\n    /// &lt;/summary&gt;\r\n    /// &lt;param name=&quot;buffer&quot;&gt;An array of bytes. This method copies &lt;paramref name=&quot;count&quot;/&gt; bytes from &lt;paramref name=&quot;buffer&quot;/&gt; to the current stream. \r\n    /// &lt;/param&gt;&lt;param name=&quot;offset&quot;&gt;The zero-based byte offset in &lt;paramref name=&quot;buffer&quot;/&gt; at which to begin copying bytes to the current stream. \r\n    /// &lt;/param&gt;&lt;param name=&quot;count&quot;&gt;The number of bytes to be written to the current stream. \r\n    /// &lt;/param&gt;&lt;exception cref=&quot;T:System.ArgumentException&quot;&gt;The sum of &lt;paramref name=&quot;offset&quot;/&gt; and &lt;paramref name=&quot;count&quot;/&gt; is greater than the buffer length. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ArgumentNullException&quot;&gt;&lt;paramref name=&quot;buffer&quot;/&gt; is null. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ArgumentOutOfRangeException&quot;&gt;&lt;paramref name=&quot;offset&quot;/&gt; or &lt;paramref name=&quot;count&quot;/&gt; is negative. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.IO.IOException&quot;&gt;An I/O error occurs. Also thrown if an attempt to write\r\n    /// beyond the block size is made.\r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.NotSupportedException&quot;&gt;The stream does not support writing. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ObjectDisposedException&quot;&gt;Methods were called after the stream was closed. \r\n    /// &lt;/exception&gt;&lt;filterpriority&gt;1&lt;/filterpriority&gt;\r\n    public override void Write(byte[] buffer, int offset, int count)\r\n    {\r\n      ValidateReadWriteParams(buffer, offset, count);\r\n\r\n      long remaining = ChunkSize - Position;\r\n      if(count &gt; remaining)\r\n      {\r\n        string msg = &quot;Blocked attempt to write block of [{0}] bytes - write goes beyond the current chunk. Chunk size is [{1}], stream position is [{2}].&quot;;\r\n        msg = String.Format(msg, count, ChunkSize, Position);\r\n        throw new IOException(msg);\r\n      }\r\n\r\n      //do not read further than the chunk\r\n      int bytesToWrite = (int)Math.Min(count, ChunkSize - Position);\r\n\r\n      //if there is nothing to write, really, don't invoke the stream\r\n      if (bytesToWrite == 0) return;\r\n\r\n      //write data and advance position\r\n      DecoratedStream.Write(buffer, offset, bytesToWrite);\r\n      position += bytesToWrite;\r\n    }\r\n\r\n\r\n    private static void ValidateReadWriteParams(byte[] buffer, int offset, int count)\r\n    {\r\n      if (buffer == null) throw new ArgumentNullException(&quot;buffer&quot;);\r\n\r\n      if (offset &lt; 0)\r\n      {\r\n        throw new ArgumentOutOfRangeException(&quot;offset&quot;, &quot;Offset cannot be negative.&quot;);\r\n      }\r\n      if (count &lt; 0)\r\n      {\r\n        throw new ArgumentOutOfRangeException(&quot;count&quot;, &quot;Number of bytes to read cannot be negative.&quot;);\r\n      }\r\n      if ((buffer.Length - offset) &lt; count)\r\n      {\r\n        throw new ArgumentException(&quot;Invalid offset length.&quot;);\r\n      }\r\n    }\r\n\r\n\r\n    public override void Flush()\r\n    {\r\n      DecoratedStream.Flush();\r\n    }\r\n\r\n    /// &lt;summary&gt;\r\n    /// When overridden in a derived class, sets the position within the current stream.\r\n    /// &lt;/summary&gt;\r\n    /// &lt;returns&gt;\r\n    /// The new position within the current stream.\r\n    /// &lt;/returns&gt;\r\n    /// &lt;param name=&quot;offset&quot;&gt;A byte offset relative to the &lt;paramref name=&quot;origin&quot;/&gt; parameter. \r\n    /// &lt;/param&gt;&lt;param name=&quot;origin&quot;&gt;A value of type &lt;see cref=&quot;T:System.IO.SeekOrigin&quot;/&gt; indicating the reference point used to obtain the new position. \r\n    /// &lt;/param&gt;&lt;exception cref=&quot;T:System.IO.IOException&quot;&gt;An I/O error occurs. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.NotSupportedException&quot;&gt;The stream does not support seeking, such as if the stream is constructed from a pipe or console output. \r\n    /// &lt;/exception&gt;&lt;exception cref=&quot;T:System.ObjectDisposedException&quot;&gt;Methods were called after the stream was closed. \r\n    /// &lt;/exception&gt;&lt;filterpriority&gt;1&lt;/filterpriority&gt;\r\n    public override long Seek(long offset, SeekOrigin origin)\r\n    {\r\n      long proposedPosition;\r\n\r\n      switch(origin)\r\n      {\r\n        case SeekOrigin.Begin:\r\n          proposedPosition = offset;\r\n          break;\r\n        case SeekOrigin.Current:\r\n          proposedPosition = Position + offset;\r\n          break;\r\n        case SeekOrigin.End:\r\n          proposedPosition = ChunkSize + offset;\r\n          break;\r\n        default:\r\n          throw new ArgumentOutOfRangeException(&quot;origin&quot;);\r\n      }\r\n\r\n      if(proposedPosition &lt; 0 || proposedPosition &gt; ChunkSize)\r\n      {\r\n        throw new IOException(&quot;Invalid offset with regards to origin&quot;);\r\n      }\r\n\r\n      Position = proposedPosition;\r\n      return proposedPosition;\r\n    }\r\n\r\n    public override void SetLength(long value)\r\n    {\r\n      throw new NotSupportedException();\r\n    }\r\n\r\n\r\n    public override bool CanRead\r\n    {\r\n      get { return DecoratedStream.CanRead; }\r\n    }\r\n\r\n    public override bool CanSeek\r\n    {\r\n      get { return DecoratedStream.CanSeek; }\r\n    }\r\n\r\n    public override bool CanWrite\r\n    {\r\n      get { return DecoratedStream.CanWrite; }\r\n    }\r\n\r\n    public override long Length\r\n    {\r\n      get { return ChunkSize; }\r\n    }\r\n  }\r\n}\r\n</pre>",
    "PostedDate": "2010-04-19T11:36:19.977-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432822",
    "ThreadId": "209615",
    "Html": "<p>@hardcodet - I'm probably missing something subtle, but wouldn't a regular FileStream do the same thing with chunking in this scenario instead of the MemoryStream in my example? The zip library seems to stream the input and output rather than load everything in one go...</p>",
    "PostedDate": "2010-04-19T11:52:11.51-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432829",
    "ThreadId": "209615",
    "Html": "Pointy,\r\n\r\nAbsolutely - if I've read the code correctly, the ChunkStream does pretty much the same thing as your implementation (hiding the real stream, and just presenting a chunk of data as a stream that can be addressed with absolute and relative positions). The difference is that the ChunkStream is a bit more generic (you can wrap the original stream with a single line of code), and it might be a bit more resource friendly in terms of memory consumption in case you're dealing with big files (you wouldn't want hundreds of MB in RAM). But nothing wrong with your approach :)",
    "PostedDate": "2010-04-19T12:15:24.027-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "432938",
    "ThreadId": "209615",
    "Html": "<p>Cool - pretty much what I thought.&nbsp;I was just checking there wasn't a gotcha hiding somewhere...</p>",
    "PostedDate": "2010-04-19T16:38:04.73-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "433857",
    "ThreadId": "209615",
    "Html": "<p>Of course I can work around the issue by making ZipFile believe it is reading/writing at the beginning of the stream.</p>\r\n<p>But I think the documentation stated that I should be able to read/write at any position in a stream. And normal archivers being able to open my file is definitely nice, but not necessary.</p>\r\n<p>ZipFile.Write seems to get the current position of the stream and use that as the starting offset for absolute adresses in the zipfile. ZipFile.Read on the other hand seems to use 0 always.</p>\r\n<p>IMO the Read behaviour is better as default, since Position is not a well defined concept in all streams. Instead I should be able to pass that start offset manually to ZipFile.<br>Then I can use any position I need in the cases where I want to be compatible with other archivers, and get position indepence(which is a very desirable feature IMO) by default.</p>\r\n<p>I would have prefered if zip only used relative adresses, but without a time-machine it'll be hard to fix that.</p>",
    "PostedDate": "2010-04-21T13:39:10.713-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "433875",
    "ThreadId": "209615",
    "Html": "<div style=\"width:800px\">\r\n<p>MoC,</p>\r\n<p>just catching up.</p>\r\n<p>You need to NOT seek when reading the ZipFile.&nbsp;&nbsp; A zipfile can have arbitrary stuff in the front of the file.&nbsp; It will be readable by winzip and other tools.&nbsp; It is also readable by DotNetZip when structured in this way.&nbsp; You don't need to seek to the &quot;start&quot; of the zip file, to successfully read it with DotNetZip.</p>\r\n<p>The way to think about it - the zipfile has offsets stored within it, in the &quot;central directory&quot;.&nbsp; These offsets point to particular places in the file.&nbsp; If you write 64 bytes of data, or any amount of data, into a file, then save a zipfile into the same&nbsp;FileStream, the offsets that DotNetZip writes will be correct.</p>\r\n<p>If you attempt to read a valid zipfile starting at byte 64, the offsets will be wrong.</p>\r\n<p>I don't know if this clears things up, but. .. . . Just don't seek when you read.</p>\r\n<p>&nbsp;</p>\r\n<p>&nbsp;</p>\r\n</div>",
    "PostedDate": "2010-04-21T14:07:54.827-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "433883",
    "ThreadId": "209615",
    "Html": "<div style=\"width:800px\">\r\n<p>Let me offer some additional description.</p>\r\n<p>Suppose you open a FileStream and write 100 bytes of data.&nbsp; At that point you also Save a ZipFile to that same stream.&nbsp; The result is a valid zip file with 100 bytes of data in front of it.&nbsp; It should be readable by any tool.&nbsp; It will be readable as a zipfile with DotNetZIp. There is no special seek required.</p>\r\n<p>Suppose you open a MemoryStream and save a ZipFile into it.&nbsp; Then you open a FileStream and write 100 bytes of data.&nbsp; Then you copy to that FileStream, all the data from the MemoryStream.&nbsp; The result is a bytestream with 100 bytes of something, followed by the contents of a zip file.&nbsp; It will not be readable as a zipfile by any tool.&nbsp; If you want to read it with DotNetZip, you need to open the file, seek forward 100 bytes, then read.</p>\r\n<p>If this behavior seems illogical or inconsistent to you, you can open a workitem and suggest changes.&nbsp;&nbsp;So far I don't see the problem, though.&nbsp; I see that Save() works differently than Read(), with respect to the offsets.&nbsp; I don't see that as a huge problem though.&nbsp;</p>\r\n<p>The reason it works the way it does, is because of the ZipFile structure itself.&nbsp; Some zips require this format.&nbsp; For example, self-extracting archives.&nbsp; This approach also allows easy creation of self-extracting archives, whereas using a zero-based offset on Save() would&nbsp;not.&nbsp; &nbsp;</p>\r\n<p><span style=\"color:#ff0000\"><strong>Update</strong></span>: <span style=\"color:#ff0000\">ok, I can see a case where you have an open stream, and you'd like to just write the zip file into it, without getting the automagic offset re-calculation.&nbsp;&nbsp;</span></p>\r\n<p><span style=\"color:#000000\">The way you could do this is to define an OffsetStream that does this for you.&nbsp; I can't think of a way to do it directly with DotNetZip currently, without caching the content in a MemoryStream.&nbsp; I think it's a reasonable request.</span></p>\r\n<p><span style=\"color:#000000\">&nbsp;</span></p>\r\n</div>",
    "PostedDate": "2010-04-21T14:20:27.58-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "433899",
    "ThreadId": "209615",
    "Html": "This discussion has been copied to a work item. Click <a href=\"http://dotnetzip.codeplex.com/WorkItem/View.aspx?WorkItemId=10684\">here</a> to go to the work item and continue the discussion.",
    "PostedDate": "2010-04-21T14:52:36.903-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  }
]