problem saving zip file to multiple streams

Jul 7, 2011 at 6:16 PM

Im not sure what the source of this problem is. I made a class derived from Stream which just acts as one Stream but writes to a FileStream and a NetworkStream in one shot. The problem is when the zipping comes accross a file that is either in use or does not exists, it fires the ZipError event but then throws an exception for the same file, even though I set the ZipErrorAction to Skip. If I do not use the MultiWriteStream and leave the rest of the code the same this problem does not occur. Here is a simplified version of my code which causes the problem:

public class Program
{
    static void Main()
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp);
        socket.Connect("localhost", 1500);
        using (var ns = new NetworkStream(socket))
        {
            using (var fs = File.Create("C:\\test.zip"))
            {
                using (var zip = new ZipFile())
                {
                    zip.ZipError += ZipError;
                    zip.AddFile("C:\\FileInUse.txt");
                    using (var stream = new MultiWriteStream(ns, fs))
                    {
                        zip.Save(stream);
                    }
                }
            }
        }
    }

    public static void ZipError(object sender, ZipErrorEventArgs e)
    {
        e.CurrentEntry.ZipErrorAction = ZipErrorAction.Skip;
    }
}
public class MultiWriteStream : Stream
{
    NetworkStream netStream;
    FileStream fileStream;

    public MultiWriteStream(NetworkStream netStream, FileStream fileStream)
    {
        this.netStream = netStream;
        this.fileStream = fileStream;
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        netStream.Write(buffer, offset, count);
        fileStream.Write(buffer, offset, count);
    }

    public override bool CanRead
    {
        get { return false; }
    }
    public override bool CanSeek
    {
        get { return false; }
    }
    public override bool CanWrite
    {
        get { return (netStream.CanWrite && fileStream.CanWrite); }
    }
    public override long Length
    {
        get { throw new NotSupportedException(); }
    }
    public override long Position
    {
        get
        {
            throw new NotSupportedException();
        }
        set
        {
            throw new NotSupportedException();
        }
    }
    public override void Flush()
    {
        netStream.Flush();
        fileStream.Flush();
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }
    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException("This is a write only stream");
    }
    public override void Close()
    {
        netStream.Close();
        fileStream.Close();
    }
}


Any idea why this problem occurs? If not is there a better way for me to write to 2 simultaneous streams?

thanks for any help

Coordinator
Jul 7, 2011 at 6:56 PM

What's the exception?  can you show it?

 

Jul 7, 2011 at 7:15 PM

When the file is in use it gets the "File is being used by another process" and if the file is missing it gets a FileNotFound Exception. So its basically throwing the exception twice but one is being caught by the ZipError event but the other one is being thrown from ZipFile.Save

Coordinator
Jul 8, 2011 at 5:21 PM

Hey Tacker -

here's the problem.  When DotNetZip encounters an error opening a file, it invokes your error handler, which tells DotNetZIp to skip that entry.  In the general case, DotNetZIp needs to seek back in the output stream, to "erase" any output that has already been emitted for that entry.  Your output stream is non-seekable, which is why it fails.  

Now, In your particular case, there is actually no need to seek backward.  In your case, the initial open of the file failed (because the file is locked, in-use), so no output was sent to the output stream at all, therefore there is no seekback required.  This is a bug in DotNetZip.  I'll change it so that it will try to seek back only if necessary.  With that change, DotNetZip will skip the entry if it fails to open, but will still throw an exception, even ify ou want to skip, in the event an IO error is received after open (during read of the source file). 

I hope this is clear; ask if not.

 

Coordinator
Jul 8, 2011 at 5:25 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Jul 8, 2011 at 8:27 PM

That makes sense, thanks for opening a work item, I really appreciate it.

Coordinator
Jul 11, 2011 at 10:59 PM

Brian, FYI: there's now a prelim binary available for the v1.9.1.6 release. It includes the fix for this.