WCF: Streaming a zip file, decompressing it on the fly

Dec 2, 2009 at 8:34 PM

Hello there

 

This is what I am trying to do:

I have a WCF service, which I want to transfer a huge file to the client. It uses ChunkingChannel and Streaming to do that:

 

[OperationContract]

public Stream DownloadLargeFile();

 

(I do not have the code at hand, since I am at home at the moment).

The method opens a ZIP-File via FileStream, and returns that FileStream. That's what happens server-side.

 

Client-side I want to open that stream via the ZipFile()-constructor (I found in the examples that it can take a stream as parameter), and extract all files to the client harddisk.

 

What happens now is that it throws an NotSupportedException. Seems that the Stream-class does not have the Peek() method implemeted. I have seen that DotNetZip had a quite similar problem some time ago in issue #7742. This has been related to AddFileFromStream() and has been fixed.

 

Maybe you could help me with this issue? If you need more code, callstack or the whole exeption, I can provide you with those information in the morning.

Coordinator
Dec 3, 2009 at 2:15 AM

Definitely would need to have the full exception to understand the nature of the problem.

I haven't tried ZipFile.Read() on a forward-only stream.   If you are telling me that it fails on a Seek() (not Peek) I can believe that.   To solve that, if I were you I might try wrapping a BufferedStream around the stream you have - it may provide a Seek() where DotNetZip expects one.

There is also an alternaive class, ZipInputStream, which is new for v1.9.  It offers a read-forward stream model on a zip stream.   If you can give up the random-access capability of ZipFile, ZipInputStream may be just the thing.  Just to be clear, by "Random access"  I mean, once you have a ZipFile instance, you can reference any ZipEntry by name or by numeric index, you can extract or remove them, and so on.  All this stuff requires Seek() on the source stream.  ZipInputStream reads forward only, so it may solve your problem, if you are willing to give up the random access.

private void Unzip(Stream rawStream)
{
    byte[] buffer= new byte[2048];
    int n;
    using (var input= new ZipInputStream(rawStream))
    {
        ZipEntry e;
        while (( e = input.GetNextEntry()) != null)
        {
            // Here you can decide whether to read/extract the current 
            // entry.  Regardless, once you call GetNextEntry(), 
            // you cannot return to the entry you've "passed over". 
            if (e.IsDirectory) continue;
            string outputPath = Path.Combine(extractDir, e.FileName);
            using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
            {
                while ((n= input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    output.Write(buffer,0,n);
                }
            }
        }
    }
}

You need DotNetZip v1.9 to get the ZipInputStream. 

 

Dec 3, 2009 at 9:19 AM

Cheeno: Thanks for your fast reply, excellent support of an excellent project!

Here is the full exception:

 

System.NotSupportedException: Die angegebene Methode wird nicht unterstützt.
   bei System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Seek(Int
64 offset, SeekOrigin origin)
   bei Ionic.Zip.ZipEntry.HandlePK00Prefix(Stream s) in c:\dinoch\dev\dotnet\zip
\DotNetZip\Zip Partial DLL\ZipEntry.Read.cs:Zeile 392.
   bei Ionic.Zip.ZipEntry.ReadEntry(ZipContainer zc, Boolean first) in c:\dinoch
\dev\dotnet\zip\DotNetZip\Zip Partial DLL\ZipEntry.Read.cs:Zeile 350.
   bei Ionic.Zip.ZipFile.ReadIntoInstance_Orig(ZipFile zf) in c:\dinoch\dev\dotn
et\zip\DotNetZip\Zip Partial DLL\ZipFile.Read.cs:Zeile 1207.
   bei Ionic.Zip.ZipFile.ReadIntoInstance(ZipFile zf) in c:\dinoch\dev\dotnet\zi
p\DotNetZip\Zip Partial DLL\ZipFile.Read.cs:Zeile 1079.
   bei Ionic.Zip.ZipFile.Read(Stream zipStream, TextWriter statusMessageWriter,
Encoding encoding, EventHandler`1 readProgress) in c:\dinoch\dev\dotnet\zip\DotN
etZip\Zip Partial DLL\ZipFile.Read.cs:Zeile 849.
   bei Ionic.Zip.ZipFile.Read(Stream zipStream, TextWriter statusMessageWriter,
Encoding encoding) in c:\dinoch\dev\dotnet\zip\DotNetZip\Zip Partial DLL\ZipFile
.Read.cs:Zeile 781.
   bei Ionic.Zip.ZipFile.Read(Stream zipStream) in c:\dinoch\dev\dotnet\zip\DotN
etZip\Zip Partial DLL\ZipFile.Read.cs:Zeile 504.
   bei MovieFetcher.CMovieCheckerProcess.DownloadClientDataTest() in D:\project\
battleforge\release\beta\tool\ServerMovieAutomation\MovieFetcher\MovieFetcher.cs
:Zeile 220.
   bei MovieFetcher.CMovieCheckerProcess.ProcessMovieCallback(Object _oState) in
 D:\project\battleforge\release\beta\tool\ServerMovieAutomation\MovieFetcher\Mov
ieFetcher.cs:Zeile 234.
   bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext,
ContextCallback callback, Object state)
   bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_Thr
eadPoolWaitCallback tpWaitCallBack)
   bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state
)

I will have a look at ZipInputStream, I already saw that in 1.9 beta. In this case, I do only need to exctract all the files in the ZIP-file, so no need for random access. So this might be worth a try.