[System.IO.Compression] Fixes ZipArchive to support non-seekable streams.
authorJoão Matos <joao@tritao.eu>
Sat, 2 Jul 2016 01:02:25 +0000 (02:02 +0100)
committerJoão Matos <joao@tritao.eu>
Sat, 2 Jul 2016 01:02:25 +0000 (02:02 +0100)
This is the same behavior as documented on MSDN.

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=30686.

mcs/class/System.IO.Compression/Test/System.IO.Compression/ZipTest.cs
mcs/class/System.IO.Compression/ZipArchive.cs

index b9bb81011857ee564ee4c5c2c63cabd157e9f2c9..2d03d559d4e08d402254d2a9d739f0c290484f35 100644 (file)
@@ -480,5 +480,24 @@ namespace MonoTests.System.IO.Compression
                        }
                        File.Delete ("empty.zip");
                }
+
+               class MyFakeStream : FileStream 
+               {
+                       public MyFakeStream (string path, FileMode mode) : base(path, mode) {}
+
+                       /// <summary>
+                       /// Simulate "CanSeek" is false, which is the case when you are retreiving data from web.
+                       /// </summary>
+                       public override bool CanSeek => false;
+               }
+
+               [Test]
+               public void ZipReadNonSeekableStream()
+               {
+                       var stream = new MyFakeStream("test.nupkg", FileMode.Open);
+                       using (var archive = new ZipArchive (stream, ZipArchiveMode.Read))
+                       {
+                       }
+               }
        }
 }
index 9dbf04cd339ccdc84c09cf00af7eb7546f28f66b..c1a3df57566020c08cf0509ddfc9f44c097dfe87 100644 (file)
@@ -49,7 +49,7 @@ namespace System.IO.Compression
 
                        this.stream = stream;
                        mode = ZipArchiveMode.Read;
-                       CreateZip(stream, mode);
+                       CreateZip(mode);
                }
 
                public ZipArchive (Stream stream, ZipArchiveMode mode)
@@ -59,7 +59,7 @@ namespace System.IO.Compression
 
                        this.stream = stream;
                        this.mode = mode;
-                       CreateZip(stream, mode);
+                       CreateZip(mode);
                }
 
                public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen)
@@ -70,7 +70,7 @@ namespace System.IO.Compression
                        this.stream = stream;
                        this.mode = mode;
                        leaveStreamOpen = leaveOpen;
-                       CreateZip(stream, mode);
+                       CreateZip(mode);
                }
 
                public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding entryNameEncoding)
@@ -82,10 +82,10 @@ namespace System.IO.Compression
                        this.mode = mode;
                        leaveStreamOpen = leaveOpen;
                        this.entryNameEncoding = entryNameEncoding;
-                       CreateZip(stream, mode);
+                       CreateZip(mode);
                }
 
-               private void CreateZip(Stream stream, ZipArchiveMode mode)
+               private void CreateZip(ZipArchiveMode mode)
                {
                        try {
                                if (mode != ZipArchiveMode.Read && mode != ZipArchiveMode.Create && mode != ZipArchiveMode.Update)
@@ -103,6 +103,18 @@ namespace System.IO.Compression
                                if (mode == ZipArchiveMode.Update && (!stream.CanRead || !stream.CanWrite || !stream.CanSeek))
                                        throw new ArgumentException("Stream must support reading, writing and seeking for Update archive mode");
 
+                               // If the stream is not seekable, then buffer it into memory (same behavior as .NET). 
+                               if (mode == ZipArchiveMode.Read && !stream.CanSeek)
+                               {
+                                       var memoryStream = new MemoryStream();
+                                       stream.CopyTo(memoryStream);
+
+                                       if (!leaveStreamOpen)
+                                               stream.Dispose();
+
+                                       this.stream = memoryStream;
+                               }
+
                                try {
                                        zipFile = mode != ZipArchiveMode.Create && stream.Length != 0
                                                ? SharpCompress.Archive.Zip.ZipArchive.Open(stream)