AddEntry Method (entryName, writer)

DotNetZip

Ionic Zip Library v1.9.1.6 AddEntry Method (entryName, writer)
ReferenceIonic.ZipZipFileAddEntry(String, WriteDelegate)
Add a ZipEntry for which content is written directly by the application.
Declaration Syntax
C# Visual Basic Visual C++
public ZipEntry AddEntry(
	string entryName,
	WriteDelegate writer
)
Public Function AddEntry ( _
	entryName As String, _
	writer As WriteDelegate _
) As ZipEntry
public:
ZipEntry^ AddEntry(
	String^ entryName, 
	WriteDelegate^ writer
)
Parameters
entryName (String)
the name of the entry to add
writer (WriteDelegate)
the delegate which will write the entry content
Return Value
the ZipEntry added
Remarks

When the application needs to write the zip entry data, use this method to add the ZipEntry. For example, in the case that the application wishes to write the XML representation of a DataSet into a ZipEntry, the application can use this method to do so.

For ZipFile properties including Encryption, Password, SetCompression, ProvisionalAlternateEncoding, ExtractExistingFile, ZipErrorAction, and CompressionLevel, their respective values at the time of this call will be applied to the ZipEntry added.

About progress events: When using the WriteDelegate, DotNetZip does not issue any SaveProgress events with EventType = Saving_EntryBytesRead. (This is because it is the application's code that runs in WriteDelegate - there's no way for DotNetZip to know when to issue a EntryBytesRead event.) Applications that want to update a progress bar or similar status indicator should do so from within the WriteDelegate itself. DotNetZip will issue the other SaveProgress events, including Saving_Started, Saving_BeforeWriteEntry, and Saving_AfterWriteEntry.

Note: When you use PKZip encryption, it's normally necessary to compute the CRC of the content to be encrypted, before compressing or encrypting it. Therefore, when using PKZip encryption with a WriteDelegate, the WriteDelegate CAN BE called twice: once to compute the CRC, and the second time to potentially compress and encrypt. Surprising, but true. This is because PKWARE specified that the encryption initialization data depends on the CRC. If this happens, for each call of the delegate, your application must stream the same entry data in its entirety. If your application writes different data during the second call, it will result in a corrupt zip file.

The double-read behavior happens with all types of entries, not only those that use WriteDelegate. It happens if you add an entry from a filesystem file, or using a string, or a stream, or an opener/closer pair. But in those cases, DotNetZip takes care of reading twice; in the case of the WriteDelegate, the application code gets invoked twice. Be aware.

As you can imagine, this can cause performance problems for large streams, and it can lead to correctness problems when you use a WriteDelegate. This is a pretty big pitfall. There are two ways to avoid it. First, and most preferred: don't use PKZIP encryption. If you use the WinZip AES encryption, this problem doesn't occur, because the encryption protocol doesn't require the CRC up front. Second: if you do choose to use PKZIP encryption, write out to a non-seekable stream (like standard output, or the Response.OutputStream in an ASP.NET application). In this case, DotNetZip will use an alternative encryption protocol that does not rely on the CRC of the content. This also implies setting bit 3 in the zip entry, which still presents problems for some zip tools.

In the future I may modify DotNetZip to *always* use bit 3 when PKZIP encryption is in use. This seems like a win overall, but there will be some work involved. If you feel strongly about it, visit the DotNetZip forums and vote up the Workitem tracking this issue.

Examples
This example shows an application filling a DataSet, then saving the contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an anonymous delegate in C#. The DataSet XML is never saved to a disk file.
CopyC#
var c1= new System.Data.SqlClient.SqlConnection(connstring1);
var da = new System.Data.SqlClient.SqlDataAdapter()
    {
        SelectCommand=  new System.Data.SqlClient.SqlCommand(strSelect, c1)
    };

DataSet ds1 = new DataSet();
da.Fill(ds1, "Invoices");

using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
{
    zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) );
    zip.Save(zipFileName);
}
Examples
This example uses an anonymous method in C# as the WriteDelegate to provide the data for the ZipEntry. The example is a bit contrived - the AddFile() method is a simpler way to insert the contents of a file into an entry in a zip file. On the other hand, if there is some sort of processing or transformation of the file contents required before writing, the application could use the WriteDelegate to do it, in this way.
CopyC#
using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ))
{
    using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
    {
        zip.AddEntry(zipEntryName, (name,output) =>
            {
                byte[] buffer = new byte[BufferSize];
                int n;
                while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
                {
                    // could transform the data here...
                    output.Write(buffer, 0, n);
                    // could update a progress bar here
                }
            });

        zip.Save(zipFileName);
    }
}
Examples
This example uses a named delegate in VB to write data for the given ZipEntry (VB9 does not have anonymous delegates). The example here is a bit contrived - a simpler way to add the contents of a file to a ZipEntry is to simply use the appropriate AddFile() method. The key scenario for which the WriteDelegate makes sense is saving a DataSet, in XML format, to the zip file. The DataSet can write XML to a stream, and the WriteDelegate is the perfect place to write into the zip file. There may be other data structures that can write to a stream, but cannot be read as a stream. The WriteDelegate would be appropriate for those cases as well.
CopyVB.NET
Private Sub WriteEntry (ByVal name As String, ByVal output As Stream)
    Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        Dim n As Integer = -1
        Dim buffer As Byte() = New Byte(BufferSize){}
        Do While n <> 0
            n = input.Read(buffer, 0, buffer.Length)
            output.Write(buffer, 0, n)
        Loop
    End Using
End Sub

Public Sub Run()
    Using zip = New ZipFile
        zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
        zip.Save(zipFileName)
    End Using
End Sub

Assembly: Ionic.Zip (Module: Ionic.Zip) Version: 1.9.1.8 (1.9.1.8)