/// <summary> /// Add a ZipEntry for which content is written directly by the application. /// </summary> /// /// <remarks> /// <para> /// 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. /// </para> /// /// <para> /// For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see /// cref="Password"/>, <see cref="SetCompression"/>, <see /// cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>, /// <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their /// respective values at the time of this call will be applied to the /// <c>ZipEntry</c> added. /// </para> /// /// <para> /// About progress events: When using the WriteDelegate, DotNetZip does /// not issue any SaveProgress events with <c>EventType</c> = <see /// cref="ZipProgressEventType.Saving_EntryBytesRead"> /// Saving_EntryBytesRead</see>. (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 <see cref="ZipProgressEventType.Saving_Started"> /// Saving_Started</see>, /// <see cref="ZipProgressEventType.Saving_BeforeWriteEntry"> /// Saving_BeforeWriteEntry</see>, and <see /// cref="ZipProgressEventType.Saving_AfterWriteEntry"> /// Saving_AfterWriteEntry</see>. /// </para> /// /// <para> /// 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. /// </para> /// /// <para> /// 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. /// </para> /// /// <para> /// As you can imagine, this can cause performance problems for large /// streams, and it can lead to correctness problems when you use a /// <c>WriteDelegate</c>. 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. /// </para> /// /// <para> /// 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 <see /// href="http://dotnetzip.codeplex.com/workitem/13686">the Workitem /// tracking this issue</see>. /// </para> /// /// </remarks> /// /// <param name="entryName">the name of the entry to add</param> /// <param name="writer">the delegate which will write the entry content</param> /// <returns>the ZipEntry added</returns> /// /// <example> /// /// 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. /// /// <code lang="C#"> /// 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); /// } /// </code> /// </example> /// /// <example> /// /// 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 /// <c>AddFile()</c> 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 <c>WriteDelegate</c> to do it, in this way. /// /// <code lang="C#"> /// 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); /// } /// } /// </code> /// </example> /// /// <example> /// /// 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 <c>AddFile()</c> method. The key scenario for /// which the <c>WriteDelegate</c> 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 <c>WriteDelegate</c> would be appropriate for those cases as /// well. /// /// <code lang="VB"> /// 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 /// </code> /// </example> public ZipEntry AddEntry(string entryName, WriteDelegate writer) { ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer); if (Verbose) { StatusMessageTextWriter.WriteLine("adding {0}...", entryName); } return(_InternalAddEntry(ze)); }