Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PausableBasicAlgorithm/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipFile.AddUpdate.cs @ 13401

Last change on this file since 13401 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 95.0 KB
Line 
1// ZipFile.AddUpdate.cs
2// ------------------------------------------------------------------
3//
4// Copyright (c) 2009-2011 Dino Chiesa.
5// All rights reserved.
6//
7// This code module is part of DotNetZip, a zipfile class library.
8//
9// ------------------------------------------------------------------
10//
11// This code is licensed under the Microsoft Public License.
12// See the file License.txt for the license details.
13// More info on: http://dotnetzip.codeplex.com
14//
15// ------------------------------------------------------------------
16//
17// last saved (in emacs):
18// Time-stamp: <2011-November-01 13:56:58>
19//
20// ------------------------------------------------------------------
21//
22// This module defines the methods for Adding and Updating entries in
23// the ZipFile.
24//
25// ------------------------------------------------------------------
26//
27
28
29using System;
30using System.IO;
31using System.Collections.Generic;
32
33namespace OfficeOpenXml.Packaging.Ionic.Zip
34{
35    internal partial class ZipFile
36    {
37        /// <summary>
38        ///   Adds an item, either a file or a directory, to a zip file archive.
39        /// </summary>
40        ///
41        /// <remarks>
42        /// <para>
43        ///   This method is handy if you are adding things to zip archive and don't
44        ///   want to bother distinguishing between directories or files.  Any files are
45        ///   added as single entries.  A directory added through this method is added
46        ///   recursively: all files and subdirectories contained within the directory
47        ///   are added to the <c>ZipFile</c>.
48        /// </para>
49        ///
50        /// <para>
51        ///   The name of the item may be a relative path or a fully-qualified
52        ///   path. Remember, the items contained in <c>ZipFile</c> instance get written
53        ///   to the disk only when you call <see cref="ZipFile.Save()"/> or a similar
54        ///   save method.
55        /// </para>
56        ///
57        /// <para>
58        ///   The directory name used for the file within the archive is the same
59        ///   as the directory name (potentially a relative path) specified in the
60        ///   <paramref name="fileOrDirectoryName"/>.
61        /// </para>
62        ///
63        /// <para>
64        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
65        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
66        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
67        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
68        ///   respective values at the time of this call will be applied to the
69        ///   <c>ZipEntry</c> added.
70        /// </para>
71        ///
72        /// </remarks>
73        ///
74        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
75        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
76        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
77        ///
78        /// <overloads>This method has two overloads.</overloads>
79        /// <param name="fileOrDirectoryName">
80        /// the name of the file or directory to add.</param>
81        ///
82        /// <returns>The <c>ZipEntry</c> added.</returns>
83        public ZipEntry AddItem(string fileOrDirectoryName)
84        {
85            return AddItem(fileOrDirectoryName, null);
86        }
87
88
89        /// <summary>
90        ///   Adds an item, either a file or a directory, to a zip file archive,
91        ///   explicitly specifying the directory path to be used in the archive.
92        /// </summary>
93        ///
94        /// <remarks>
95        /// <para>
96        ///   If adding a directory, the add is recursive on all files and
97        ///   subdirectories contained within it.
98        /// </para>
99        /// <para>
100        ///   The name of the item may be a relative path or a fully-qualified path.
101        ///   The item added by this call to the <c>ZipFile</c> is not read from the
102        ///   disk nor written to the zip file archive until the application calls
103        ///   Save() on the <c>ZipFile</c>.
104        /// </para>
105        ///
106        /// <para>
107        ///   This version of the method allows the caller to explicitly specify the
108        ///   directory path to be used in the archive, which would override the
109        ///   "natural" path of the filesystem file.
110        /// </para>
111        ///
112        /// <para>
113        ///   Encryption will be used on the file data if the <c>Password</c> has
114        ///   been set on the <c>ZipFile</c> object, prior to calling this method.
115        /// </para>
116        ///
117        /// <para>
118        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
119        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
120        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
121        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
122        ///   respective values at the time of this call will be applied to the
123        ///   <c>ZipEntry</c> added.
124        /// </para>
125        ///
126        /// </remarks>
127        ///
128        /// <exception cref="System.IO.FileNotFoundException">
129        ///   Thrown if the file or directory passed in does not exist.
130        /// </exception>
131        ///
132        /// <param name="fileOrDirectoryName">the name of the file or directory to add.
133        /// </param>
134        ///
135        /// <param name="directoryPathInArchive">
136        ///   The name of the directory path to use within the zip archive.  This path
137        ///   need not refer to an extant directory in the current filesystem.  If the
138        ///   files within the zip are later extracted, this is the path used for the
139        ///   extracted file.  Passing <c>null</c> (<c>Nothing</c> in VB) will use the
140        ///   path on the fileOrDirectoryName.  Passing the empty string ("") will
141        ///   insert the item at the root path within the archive.
142        /// </param>
143        ///
144        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
145        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
146        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string, string)"/>
147        ///
148        /// <example>
149        ///   This example shows how to zip up a set of files into a flat hierarchy,
150        ///   regardless of where in the filesystem the files originated. The resulting
151        ///   zip archive will contain a toplevel directory named "flat", which itself
152        ///   will contain files Readme.txt, MyProposal.docx, and Image1.jpg.  A
153        ///   subdirectory under "flat" called SupportFiles will contain all the files
154        ///   in the "c:\SupportFiles" directory on disk.
155        ///
156        /// <code>
157        /// String[] itemnames= {
158        ///   "c:\\fixedContent\\Readme.txt",
159        ///   "MyProposal.docx",
160        ///   "c:\\SupportFiles",  // a directory
161        ///   "images\\Image1.jpg"
162        /// };
163        ///
164        /// try
165        /// {
166        ///   using (ZipFile zip = new ZipFile())
167        ///   {
168        ///     for (int i = 1; i &lt; itemnames.Length; i++)
169        ///     {
170        ///       // will add Files or Dirs, recurses and flattens subdirectories
171        ///       zip.AddItem(itemnames[i],"flat");
172        ///     }
173        ///     zip.Save(ZipToCreate);
174        ///   }
175        /// }
176        /// catch (System.Exception ex1)
177        /// {
178        ///   System.Console.Error.WriteLine("exception: {0}", ex1);
179        /// }
180        /// </code>
181        ///
182        /// <code lang="VB">
183        ///   Dim itemnames As String() = _
184        ///     New String() { "c:\fixedContent\Readme.txt", _
185        ///                    "MyProposal.docx", _
186        ///                    "SupportFiles", _
187        ///                    "images\Image1.jpg" }
188        ///   Try
189        ///       Using zip As New ZipFile
190        ///           Dim i As Integer
191        ///           For i = 1 To itemnames.Length - 1
192        ///               ' will add Files or Dirs, recursing and flattening subdirectories.
193        ///               zip.AddItem(itemnames(i), "flat")
194        ///           Next i
195        ///           zip.Save(ZipToCreate)
196        ///       End Using
197        ///   Catch ex1 As Exception
198        ///       Console.Error.WriteLine("exception: {0}", ex1.ToString())
199        ///   End Try
200        /// </code>
201        /// </example>
202        /// <returns>The <c>ZipEntry</c> added.</returns>
203        public ZipEntry AddItem(String fileOrDirectoryName, String directoryPathInArchive)
204        {
205            if (File.Exists(fileOrDirectoryName))
206                return AddFile(fileOrDirectoryName, directoryPathInArchive);
207
208            if (Directory.Exists(fileOrDirectoryName))
209                return AddDirectory(fileOrDirectoryName, directoryPathInArchive);
210
211            throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!",
212                                                          fileOrDirectoryName));
213        }
214
215        /// <summary>
216        ///   Adds a File to a Zip file archive.
217        /// </summary>
218        /// <remarks>
219        ///
220        /// <para>
221        ///   This call collects metadata for the named file in the filesystem,
222        ///   including the file attributes and the timestamp, and inserts that metadata
223        ///   into the resulting ZipEntry.  Only when the application calls Save() on
224        ///   the <c>ZipFile</c>, does DotNetZip read the file from the filesystem and
225        ///   then write the content to the zip file archive.
226        /// </para>
227        ///
228        /// <para>
229        ///   This method will throw an exception if an entry with the same name already
230        ///   exists in the <c>ZipFile</c>.
231        /// </para>
232        ///
233        /// <para>
234        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
235        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
236        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
237        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
238        ///   respective values at the time of this call will be applied to the
239        ///   <c>ZipEntry</c> added.
240        /// </para>
241        ///
242        /// </remarks>
243        ///
244        /// <example>
245        /// <para>
246        ///   In this example, three files are added to a Zip archive. The ReadMe.txt
247        ///   file will be placed in the root of the archive. The .png file will be
248        ///   placed in a folder within the zip called photos\personal.  The pdf file
249        ///   will be included into a folder within the zip called Desktop.
250        /// </para>
251        /// <code>
252        ///    try
253        ///    {
254        ///      using (ZipFile zip = new ZipFile())
255        ///      {
256        ///        zip.AddFile("c:\\photos\\personal\\7440-N49th.png");
257        ///        zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf");
258        ///        zip.AddFile("ReadMe.txt");
259        ///
260        ///        zip.Save("Package.zip");
261        ///      }
262        ///    }
263        ///    catch (System.Exception ex1)
264        ///    {
265        ///      System.Console.Error.WriteLine("exception: " + ex1);
266        ///    }
267        /// </code>
268        ///
269        /// <code lang="VB">
270        ///  Try
271        ///       Using zip As ZipFile = New ZipFile
272        ///           zip.AddFile("c:\photos\personal\7440-N49th.png")
273        ///           zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf")
274        ///           zip.AddFile("ReadMe.txt")
275        ///           zip.Save("Package.zip")
276        ///       End Using
277        ///   Catch ex1 As Exception
278        ///       Console.Error.WriteLine("exception: {0}", ex1.ToString)
279        ///   End Try
280        /// </code>
281        /// </example>
282        ///
283        /// <overloads>This method has two overloads.</overloads>
284        ///
285        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
286        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
287        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
288        ///
289        /// <param name="fileName">
290        ///   The name of the file to add. It should refer to a file in the filesystem.
291        ///   The name of the file may be a relative path or a fully-qualified path.
292        /// </param>
293        /// <returns>The <c>ZipEntry</c> corresponding to the File added.</returns>
294        public ZipEntry AddFile(string fileName)
295        {
296            return AddFile(fileName, null);
297        }
298
299
300
301
302
303        /// <summary>
304        ///   Adds a File to a Zip file archive, potentially overriding the path to be
305        ///   used within the zip archive.
306        /// </summary>
307        ///
308        /// <remarks>
309        /// <para>
310        ///   The file added by this call to the <c>ZipFile</c> is not written to the
311        ///   zip file archive until the application calls Save() on the <c>ZipFile</c>.
312        /// </para>
313        ///
314        /// <para>
315        ///   This method will throw an exception if an entry with the same name already
316        ///   exists in the <c>ZipFile</c>.
317        /// </para>
318        ///
319        /// <para>
320        ///   This version of the method allows the caller to explicitly specify the
321        ///   directory path to be used in the archive.
322        /// </para>
323        ///
324        /// <para>
325        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
326        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
327        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
328        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
329        ///   respective values at the time of this call will be applied to the
330        ///   <c>ZipEntry</c> added.
331        /// </para>
332        ///
333        /// </remarks>
334        ///
335        /// <example>
336        /// <para>
337        ///   In this example, three files are added to a Zip archive. The ReadMe.txt
338        ///   file will be placed in the root of the archive. The .png file will be
339        ///   placed in a folder within the zip called images.  The pdf file will be
340        ///   included into a folder within the zip called files\docs, and will be
341        ///   encrypted with the given password.
342        /// </para>
343        /// <code>
344        /// try
345        /// {
346        ///   using (ZipFile zip = new ZipFile())
347        ///   {
348        ///     // the following entry will be inserted at the root in the archive.
349        ///     zip.AddFile("c:\\datafiles\\ReadMe.txt", "");
350        ///     // this image file will be inserted into the "images" directory in the archive.
351        ///     zip.AddFile("c:\\photos\\personal\\7440-N49th.png", "images");
352        ///     // the following will result in a password-protected file called
353        ///     // files\\docs\\2008-Regional-Sales-Report.pdf  in the archive.
354        ///     zip.Password = "EncryptMe!";
355        ///     zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf", "files\\docs");
356        ///     zip.Save("Archive.zip");
357        ///   }
358        /// }
359        /// catch (System.Exception ex1)
360        /// {
361        ///   System.Console.Error.WriteLine("exception: {0}", ex1);
362        /// }
363        /// </code>
364        ///
365        /// <code lang="VB">
366        ///   Try
367        ///       Using zip As ZipFile = New ZipFile
368        ///           ' the following entry will be inserted at the root in the archive.
369        ///           zip.AddFile("c:\datafiles\ReadMe.txt", "")
370        ///           ' this image file will be inserted into the "images" directory in the archive.
371        ///           zip.AddFile("c:\photos\personal\7440-N49th.png", "images")
372        ///           ' the following will result in a password-protected file called
373        ///           ' files\\docs\\2008-Regional-Sales-Report.pdf  in the archive.
374        ///           zip.Password = "EncryptMe!"
375        ///           zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf", "files\documents")
376        ///           zip.Save("Archive.zip")
377        ///       End Using
378        ///   Catch ex1 As Exception
379        ///       Console.Error.WriteLine("exception: {0}", ex1)
380        ///   End Try
381        /// </code>
382        /// </example>
383        ///
384        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string,string)"/>
385        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
386        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
387        ///
388        /// <param name="fileName">
389        ///   The name of the file to add.  The name of the file may be a relative path
390        ///   or a fully-qualified path.
391        /// </param>
392        ///
393        /// <param name="directoryPathInArchive">
394        ///   Specifies a directory path to use to override any path in the fileName.
395        ///   This path may, or may not, correspond to a real directory in the current
396        ///   filesystem.  If the files within the zip are later extracted, this is the
397        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
398        ///   VB) will use the path on the fileName, if any.  Passing the empty string
399        ///   ("") will insert the item at the root path within the archive.
400        /// </param>
401        ///
402        /// <returns>The <c>ZipEntry</c> corresponding to the file added.</returns>
403        public ZipEntry AddFile(string fileName, String directoryPathInArchive)
404        {
405            string nameInArchive = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
406            ZipEntry ze = ZipEntry.CreateFromFile(fileName, nameInArchive);
407            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", fileName);
408            return _InternalAddEntry(ze);
409        }
410
411
412        /// <summary>
413        ///   This method removes a collection of entries from the <c>ZipFile</c>.
414        /// </summary>
415        ///
416        /// <param name="entriesToRemove">
417        ///   A collection of ZipEntry instances from this zip file to be removed. For
418        ///   example, you can pass in an array of ZipEntry instances; or you can call
419        ///   SelectEntries(), and then add or remove entries from that
420        ///   ICollection&lt;ZipEntry&gt; (ICollection(Of ZipEntry) in VB), and pass
421        ///   that ICollection to this method.
422        /// </param>
423        ///
424        /// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
425        /// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
426        public void RemoveEntries(System.Collections.Generic.ICollection<ZipEntry> entriesToRemove)
427        {
428            if (entriesToRemove == null)
429                throw new ArgumentNullException("entriesToRemove");
430
431            foreach (ZipEntry e in entriesToRemove)
432            {
433                this.RemoveEntry(e);
434            }
435        }
436
437
438        /// <summary>
439        ///   This method removes a collection of entries from the <c>ZipFile</c>, by name.
440        /// </summary>
441        ///
442        /// <param name="entriesToRemove">
443        ///   A collection of strings that refer to names of entries to be removed
444        ///   from the <c>ZipFile</c>.  For example, you can pass in an array or a
445        ///   List of Strings that provide the names of entries to be removed.
446        /// </param>
447        ///
448        /// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
449        /// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
450        public void RemoveEntries(System.Collections.Generic.ICollection<String> entriesToRemove)
451        {
452            if (entriesToRemove == null)
453                throw new ArgumentNullException("entriesToRemove");
454
455            foreach (String e in entriesToRemove)
456            {
457                this.RemoveEntry(e);
458            }
459        }
460
461
462        /// <summary>
463        ///   This method adds a set of files to the <c>ZipFile</c>.
464        /// </summary>
465        ///
466        /// <remarks>
467        /// <para>
468        ///   Use this method to add a set of files to the zip archive, in one call.
469        ///   For example, a list of files received from
470        ///   <c>System.IO.Directory.GetFiles()</c> can be added to a zip archive in one
471        ///   call.
472        /// </para>
473        ///
474        /// <para>
475        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
476        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
477        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
478        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
479        ///   respective values at the time of this call will be applied to each
480        ///   ZipEntry added.
481        /// </para>
482        /// </remarks>
483        ///
484        /// <param name="fileNames">
485        ///   The collection of names of the files to add. Each string should refer to a
486        ///   file in the filesystem. The name of the file may be a relative path or a
487        ///   fully-qualified path.
488        /// </param>
489        ///
490        /// <example>
491        ///   This example shows how to create a zip file, and add a few files into it.
492        /// <code>
493        /// String ZipFileToCreate = "archive1.zip";
494        /// String DirectoryToZip = "c:\\reports";
495        /// using (ZipFile zip = new ZipFile())
496        /// {
497        ///   // Store all files found in the top level directory, into the zip archive.
498        ///   String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip);
499        ///   zip.AddFiles(filenames);
500        ///   zip.Save(ZipFileToCreate);
501        /// }
502        /// </code>
503        ///
504        /// <code lang="VB">
505        /// Dim ZipFileToCreate As String = "archive1.zip"
506        /// Dim DirectoryToZip As String = "c:\reports"
507        /// Using zip As ZipFile = New ZipFile
508        ///     ' Store all files found in the top level directory, into the zip archive.
509        ///     Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip)
510        ///     zip.AddFiles(filenames)
511        ///     zip.Save(ZipFileToCreate)
512        /// End Using
513        /// </code>
514        /// </example>
515        ///
516        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
517        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames)
518        {
519            this.AddFiles(fileNames, null);
520        }
521
522
523        /// <summary>
524        ///   Adds or updates a set of files in the <c>ZipFile</c>.
525        /// </summary>
526        ///
527        /// <remarks>
528        /// <para>
529        ///   Any files that already exist in the archive are updated. Any files that
530        ///   don't yet exist in the archive are added.
531        /// </para>
532        ///
533        /// <para>
534        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
535        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
536        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
537        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
538        ///   respective values at the time of this call will be applied to each
539        ///   ZipEntry added.
540        /// </para>
541        /// </remarks>
542        ///
543        /// <param name="fileNames">
544        ///   The collection of names of the files to update. Each string should refer to a file in
545        ///   the filesystem. The name of the file may be a relative path or a fully-qualified path.
546        /// </param>
547        ///
548        public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames)
549        {
550            this.UpdateFiles(fileNames, null);
551        }
552
553
554        /// <summary>
555        ///   Adds a set of files to the <c>ZipFile</c>, using the
556        ///   specified directory path in the archive.
557        /// </summary>
558        ///
559        /// <remarks>
560        /// <para>
561        ///   Any directory structure that may be present in the
562        ///   filenames contained in the list is "flattened" in the
563        ///   archive.  Each file in the list is added to the archive in
564        ///   the specified top-level directory.
565        /// </para>
566        ///
567        /// <para>
568        ///   For <c>ZipFile</c> properties including <see
569        ///   cref="Encryption"/>, <see cref="Password"/>, <see
570        ///   cref="SetCompression"/>, <see
571        ///   cref="ProvisionalAlternateEncoding"/>, <see
572        ///   cref="ExtractExistingFile"/>, <see
573        ///   cref="ZipErrorAction"/>, and <see
574        ///   cref="CompressionLevel"/>, their respective values at the
575        ///   time of this call will be applied to each ZipEntry added.
576        /// </para>
577        /// </remarks>
578        ///
579        /// <param name="fileNames">
580        ///   The names of the files to add. Each string should refer to
581        ///   a file in the filesystem.  The name of the file may be a
582        ///   relative path or a fully-qualified path.
583        /// </param>
584        ///
585        /// <param name="directoryPathInArchive">
586        ///   Specifies a directory path to use to override any path in the file name.
587        ///   Th is path may, or may not, correspond to a real directory in the current
588        ///   filesystem.  If the files within the zip are later extracted, this is the
589        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
590        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
591        ///   the empty string ("") will insert the item at the root path within the
592        ///   archive.
593        /// </param>
594        ///
595        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
596        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
597        {
598            AddFiles(fileNames, false, directoryPathInArchive);
599        }
600
601
602
603        /// <summary>
604        ///   Adds a set of files to the <c>ZipFile</c>, using the specified directory
605        ///   path in the archive, and preserving the full directory structure in the
606        ///   filenames.
607        /// </summary>
608        ///
609        /// <remarks>
610        ///
611        /// <para>
612        ///   Think of the <paramref name="directoryPathInArchive"/> as a "root" or
613        ///   base directory used in the archive for the files that get added.  when
614        ///   <paramref name="preserveDirHierarchy"/> is true, the hierarchy of files
615        ///   found in the filesystem will be placed, with the hierarchy intact,
616        ///   starting at that root in the archive. When <c>preserveDirHierarchy</c>
617        ///   is false, the path hierarchy of files is flattned, and the flattened
618        ///   set of files gets placed in the root within the archive as specified in
619        ///   <c>directoryPathInArchive</c>.
620        /// </para>
621        ///
622        /// <para>
623        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
624        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
625        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
626        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
627        ///   respective values at the time of this call will be applied to each
628        ///   ZipEntry added.
629        /// </para>
630        ///
631        /// </remarks>
632        ///
633        /// <param name="fileNames">
634        ///   The names of the files to add. Each string should refer to a file in the
635        ///   filesystem.  The name of the file may be a relative path or a
636        ///   fully-qualified path.
637        /// </param>
638        ///
639        /// <param name="directoryPathInArchive">
640        ///   Specifies a directory path to use as a prefix for each entry name.
641        ///   This path may, or may not, correspond to a real directory in the current
642        ///   filesystem.  If the files within the zip are later extracted, this is the
643        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
644        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
645        ///   the empty string ("") will insert the item at the root path within the
646        ///   archive.
647        /// </param>
648        ///
649        /// <param name="preserveDirHierarchy">
650        ///   whether the entries in the zip archive will reflect the directory
651        ///   hierarchy that is present in the various filenames.  For example, if
652        ///   <paramref name="fileNames"/> includes two paths,
653        ///   \Animalia\Chordata\Mammalia\Info.txt and
654        ///   \Plantae\Magnoliophyta\Dicotyledon\Info.txt, then calling this method
655        ///   with <paramref name="preserveDirHierarchy"/> = <c>false</c> will
656        ///   result in an exception because of a duplicate entry name, while
657        ///   calling this method with <paramref name="preserveDirHierarchy"/> =
658        ///   <c>true</c> will result in the full direcory paths being included in
659        ///   the entries added to the ZipFile.
660        /// </param>
661        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
662        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames,
663                             bool preserveDirHierarchy,
664                             String directoryPathInArchive)
665        {
666            if (fileNames == null)
667                throw new ArgumentNullException("fileNames");
668
669            _addOperationCanceled = false;
670            OnAddStarted();
671            if (preserveDirHierarchy)
672            {
673                foreach (var f in fileNames)
674                {
675                    if (_addOperationCanceled) break;
676                    if (directoryPathInArchive != null)
677                    {
678                        //string s = SharedUtilities.NormalizePath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
679                        string s = Path.GetFullPath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
680                        this.AddFile(f, s);
681                    }
682                    else
683                        this.AddFile(f, null);
684                }
685            }
686            else
687            {
688                foreach (var f in fileNames)
689                {
690                    if (_addOperationCanceled) break;
691                    this.AddFile(f, directoryPathInArchive);
692                }
693            }
694            if (!_addOperationCanceled)
695                OnAddCompleted();
696        }
697
698
699        /// <summary>
700        ///   Adds or updates a set of files to the <c>ZipFile</c>, using the specified
701        ///   directory path in the archive.
702        /// </summary>
703        ///
704        /// <remarks>
705        ///
706        /// <para>
707        ///   Any files that already exist in the archive are updated. Any files that
708        ///   don't yet exist in the archive are added.
709        /// </para>
710        ///
711        /// <para>
712        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
713        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
714        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
715        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
716        ///   respective values at the time of this call will be applied to each
717        ///   ZipEntry added.
718        /// </para>
719        /// </remarks>
720        ///
721        /// <param name="fileNames">
722        ///   The names of the files to add or update. Each string should refer to a
723        ///   file in the filesystem.  The name of the file may be a relative path or a
724        ///   fully-qualified path.
725        /// </param>
726        ///
727        /// <param name="directoryPathInArchive">
728        ///   Specifies a directory path to use to override any path in the file name.
729        ///   This path may, or may not, correspond to a real directory in the current
730        ///   filesystem.  If the files within the zip are later extracted, this is the
731        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
732        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
733        ///   the empty string ("") will insert the item at the root path within the
734        ///   archive.
735        /// </param>
736        ///
737        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
738        public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
739        {
740            if (fileNames == null)
741                throw new ArgumentNullException("fileNames");
742
743            OnAddStarted();
744            foreach (var f in fileNames)
745                this.UpdateFile(f, directoryPathInArchive);
746            OnAddCompleted();
747        }
748
749
750
751
752        /// <summary>
753        ///   Adds or Updates a File in a Zip file archive.
754        /// </summary>
755        ///
756        /// <remarks>
757        /// <para>
758        ///   This method adds a file to a zip archive, or, if the file already exists
759        ///   in the zip archive, this method Updates the content of that given filename
760        ///   in the zip archive.  The <c>UpdateFile</c> method might more accurately be
761        ///   called "AddOrUpdateFile".
762        /// </para>
763        ///
764        /// <para>
765        ///   Upon success, there is no way for the application to learn whether the file
766        ///   was added versus updated.
767        /// </para>
768        ///
769        /// <para>
770        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
771        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
772        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
773        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
774        ///   respective values at the time of this call will be applied to the
775        ///   <c>ZipEntry</c> added.
776        /// </para>
777        /// </remarks>
778        ///
779        /// <example>
780        ///
781        ///   This example shows how to Update an existing entry in a zipfile. The first
782        ///   call to UpdateFile adds the file to the newly-created zip archive.  The
783        ///   second call to UpdateFile updates the content for that file in the zip
784        ///   archive.
785        ///
786        /// <code>
787        /// using (ZipFile zip1 = new ZipFile())
788        /// {
789        ///   // UpdateFile might more accurately be called "AddOrUpdateFile"
790        ///   zip1.UpdateFile("MyDocuments\\Readme.txt");
791        ///   zip1.UpdateFile("CustomerList.csv");
792        ///   zip1.Comment = "This zip archive has been created.";
793        ///   zip1.Save("Content.zip");
794        /// }
795        ///
796        /// using (ZipFile zip2 = ZipFile.Read("Content.zip"))
797        /// {
798        ///   zip2.UpdateFile("Updates\\Readme.txt");
799        ///   zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed.";
800        ///   zip2.Save();
801        /// }
802        ///
803        /// </code>
804        /// <code lang="VB">
805        ///   Using zip1 As New ZipFile
806        ///       ' UpdateFile might more accurately be called "AddOrUpdateFile"
807        ///       zip1.UpdateFile("MyDocuments\Readme.txt")
808        ///       zip1.UpdateFile("CustomerList.csv")
809        ///       zip1.Comment = "This zip archive has been created."
810        ///       zip1.Save("Content.zip")
811        ///   End Using
812        ///
813        ///   Using zip2 As ZipFile = ZipFile.Read("Content.zip")
814        ///       zip2.UpdateFile("Updates\Readme.txt")
815        ///       zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed."
816        ///       zip2.Save
817        ///   End Using
818        /// </code>
819        /// </example>
820        ///
821        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
822        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
823        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
824        ///
825        /// <param name="fileName">
826        ///   The name of the file to add or update. It should refer to a file in the
827        ///   filesystem.  The name of the file may be a relative path or a
828        ///   fully-qualified path.
829        /// </param>
830        ///
831        /// <returns>
832        ///   The <c>ZipEntry</c> corresponding to the File that was added or updated.
833        /// </returns>
834        public ZipEntry UpdateFile(string fileName)
835        {
836            return UpdateFile(fileName, null);
837        }
838
839
840
841        /// <summary>
842        ///   Adds or Updates a File in a Zip file archive.
843        /// </summary>
844        ///
845        /// <remarks>
846        /// <para>
847        ///   This method adds a file to a zip archive, or, if the file already exists
848        ///   in the zip archive, this method Updates the content of that given filename
849        ///   in the zip archive.
850        /// </para>
851        ///
852        /// <para>
853        ///   This version of the method allows the caller to explicitly specify the
854        ///   directory path to be used in the archive.  The entry to be added or
855        ///   updated is found by using the specified directory path, combined with the
856        ///   basename of the specified filename.
857        /// </para>
858        ///
859        /// <para>
860        ///   Upon success, there is no way for the application to learn if the file was
861        ///   added versus updated.
862        /// </para>
863        ///
864        /// <para>
865        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
866        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
867        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
868        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
869        ///   respective values at the time of this call will be applied to the
870        ///   <c>ZipEntry</c> added.
871        /// </para>
872        /// </remarks>
873        ///
874        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string,string)"/>
875        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string,string)"/>
876        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
877        ///
878        /// <param name="fileName">
879        ///   The name of the file to add or update. It should refer to a file in the
880        ///   filesystem.  The name of the file may be a relative path or a
881        ///   fully-qualified path.
882        /// </param>
883        ///
884        /// <param name="directoryPathInArchive">
885        ///   Specifies a directory path to use to override any path in the
886        ///   <c>fileName</c>.  This path may, or may not, correspond to a real
887        ///   directory in the current filesystem.  If the files within the zip are
888        ///   later extracted, this is the path used for the extracted file.  Passing
889        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
890        ///   <c>fileName</c>, if any.  Passing the empty string ("") will insert the
891        ///   item at the root path within the archive.
892        /// </param>
893        ///
894        /// <returns>
895        ///   The <c>ZipEntry</c> corresponding to the File that was added or updated.
896        /// </returns>
897        public ZipEntry UpdateFile(string fileName, String directoryPathInArchive)
898        {
899            // ideally this would all be transactional!
900            var key = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
901            if (this[key] != null)
902                this.RemoveEntry(key);
903            return this.AddFile(fileName, directoryPathInArchive);
904        }
905
906
907
908
909
910        /// <summary>
911        ///   Add or update a directory in a zip archive.
912        /// </summary>
913        ///
914        /// <remarks>
915        ///   If the specified directory does not exist in the archive, then this method
916        ///   is equivalent to calling <c>AddDirectory()</c>.  If the specified
917        ///   directory already exists in the archive, then this method updates any
918        ///   existing entries, and adds any new entries. Any entries that are in the
919        ///   zip archive but not in the specified directory, are left alone.  In other
920        ///   words, the contents of the zip file will be a union of the previous
921        ///   contents and the new files.
922        /// </remarks>
923        ///
924        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
925        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
926        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
927        ///
928        /// <param name="directoryName">
929        ///   The path to the directory to be added to the zip archive, or updated in
930        ///   the zip archive.
931        /// </param>
932        ///
933        /// <returns>
934        /// The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
935        /// </returns>
936        public ZipEntry UpdateDirectory(string directoryName)
937        {
938            return UpdateDirectory(directoryName, null);
939        }
940
941
942        /// <summary>
943        ///   Add or update a directory in the zip archive at the specified root
944        ///   directory in the archive.
945        /// </summary>
946        ///
947        /// <remarks>
948        ///   If the specified directory does not exist in the archive, then this method
949        ///   is equivalent to calling <c>AddDirectory()</c>.  If the specified
950        ///   directory already exists in the archive, then this method updates any
951        ///   existing entries, and adds any new entries. Any entries that are in the
952        ///   zip archive but not in the specified directory, are left alone.  In other
953        ///   words, the contents of the zip file will be a union of the previous
954        ///   contents and the new files.
955        /// </remarks>
956        ///
957        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
958        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string,string)"/>
959        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
960        ///
961        /// <param name="directoryName">
962        ///   The path to the directory to be added to the zip archive, or updated
963        ///   in the zip archive.
964        /// </param>
965        ///
966        /// <param name="directoryPathInArchive">
967        ///   Specifies a directory path to use to override any path in the
968        ///   <c>directoryName</c>.  This path may, or may not, correspond to a real
969        ///   directory in the current filesystem.  If the files within the zip are
970        ///   later extracted, this is the path used for the extracted file.  Passing
971        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
972        ///   <c>directoryName</c>, if any.  Passing the empty string ("") will insert
973        ///   the item at the root path within the archive.
974        /// </param>
975        ///
976        /// <returns>
977        ///   The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
978        /// </returns>
979        public ZipEntry UpdateDirectory(string directoryName, String directoryPathInArchive)
980        {
981            return this.AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOrUpdate);
982        }
983
984
985
986
987
988        /// <summary>
989        ///   Add or update a file or directory in the zip archive.
990        /// </summary>
991        ///
992        /// <remarks>
993        /// <para>
994        ///   This is useful when the application is not sure or does not care if the
995        ///   item to be added is a file or directory, and does not know or does not
996        ///   care if the item already exists in the <c>ZipFile</c>. Calling this method
997        ///   is equivalent to calling <c>RemoveEntry()</c> if an entry by the same name
998        ///   already exists, followed calling by <c>AddItem()</c>.
999        /// </para>
1000        ///
1001        /// <para>
1002        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1003        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1004        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1005        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1006        ///   respective values at the time of this call will be applied to the
1007        ///   <c>ZipEntry</c> added.
1008        /// </para>
1009        /// </remarks>
1010        ///
1011        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
1012        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
1013        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
1014        ///
1015        /// <param name="itemName">
1016        ///  the path to the file or directory to be added or updated.
1017        /// </param>
1018        public void UpdateItem(string itemName)
1019        {
1020            UpdateItem(itemName, null);
1021        }
1022
1023
1024        /// <summary>
1025        ///   Add or update a file or directory.
1026        /// </summary>
1027        ///
1028        /// <remarks>
1029        /// <para>
1030        ///   This method is useful when the application is not sure or does not care if
1031        ///   the item to be added is a file or directory, and does not know or does not
1032        ///   care if the item already exists in the <c>ZipFile</c>. Calling this method
1033        ///   is equivalent to calling <c>RemoveEntry()</c>, if an entry by that name
1034        ///   exists, and then calling <c>AddItem()</c>.
1035        /// </para>
1036        ///
1037        /// <para>
1038        ///   This version of the method allows the caller to explicitly specify the
1039        ///   directory path to be used for the item being added to the archive.  The
1040        ///   entry or entries that are added or updated will use the specified
1041        ///   <c>DirectoryPathInArchive</c>. Extracting the entry from the archive will
1042        ///   result in a file stored in that directory path.
1043        /// </para>
1044        ///
1045        /// <para>
1046        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1047        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1048        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1049        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1050        ///   respective values at the time of this call will be applied to the
1051        ///   <c>ZipEntry</c> added.
1052        /// </para>
1053        /// </remarks>
1054        ///
1055        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
1056        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string, string)"/>
1057        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
1058        ///
1059        /// <param name="itemName">
1060        ///   The path for the File or Directory to be added or updated.
1061        /// </param>
1062        /// <param name="directoryPathInArchive">
1063        ///   Specifies a directory path to use to override any path in the
1064        ///   <c>itemName</c>.  This path may, or may not, correspond to a real
1065        ///   directory in the current filesystem.  If the files within the zip are
1066        ///   later extracted, this is the path used for the extracted file.  Passing
1067        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
1068        ///   <c>itemName</c>, if any.  Passing the empty string ("") will insert the
1069        ///   item at the root path within the archive.
1070        /// </param>
1071        public void UpdateItem(string itemName, string directoryPathInArchive)
1072        {
1073            if (File.Exists(itemName))
1074                UpdateFile(itemName, directoryPathInArchive);
1075
1076            else if (Directory.Exists(itemName))
1077                UpdateDirectory(itemName, directoryPathInArchive);
1078
1079            else
1080                throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!", itemName));
1081        }
1082
1083
1084
1085
1086        /// <summary>
1087        ///   Adds a named entry into the zip archive, taking content for the entry
1088        ///   from a string.
1089        /// </summary>
1090        ///
1091        /// <remarks>
1092        ///   Calling this method creates an entry using the given fileName and
1093        ///   directory path within the archive.  There is no need for a file by the
1094        ///   given name to exist in the filesystem; the name is used within the zip
1095        ///   archive only. The content for the entry is encoded using the default text
1096        ///   encoding for the machine, or on Silverlight, using UTF-8.
1097        /// </remarks>
1098        ///
1099        /// <param name="content">
1100        ///   The content of the file, should it be extracted from the zip.
1101        /// </param>
1102        ///
1103        /// <param name="entryName">
1104        ///   The name, including any path, to use for the entry within the archive.
1105        /// </param>
1106        ///
1107        /// <returns>The <c>ZipEntry</c> added.</returns>
1108        ///
1109        /// <example>
1110        ///
1111        /// This example shows how to add an entry to the zipfile, using a string as
1112        /// content for that entry.
1113        ///
1114        /// <code lang="C#">
1115        /// string Content = "This string will be the content of the Readme.txt file in the zip archive.";
1116        /// using (ZipFile zip1 = new ZipFile())
1117        /// {
1118        ///   zip1.AddFile("MyDocuments\\Resume.doc", "files");
1119        ///   zip1.AddEntry("Readme.txt", Content);
1120        ///   zip1.Comment = "This zip file was created at " + System.DateTime.Now.ToString("G");
1121        ///   zip1.Save("Content.zip");
1122        /// }
1123        ///
1124        /// </code>
1125        /// <code lang="VB">
1126        /// Public Sub Run()
1127        ///   Dim Content As String = "This string will be the content of the Readme.txt file in the zip archive."
1128        ///   Using zip1 As ZipFile = New ZipFile
1129        ///     zip1.AddEntry("Readme.txt", Content)
1130        ///     zip1.AddFile("MyDocuments\Resume.doc", "files")
1131        ///     zip1.Comment = ("This zip file was created at " &amp; DateTime.Now.ToString("G"))
1132        ///     zip1.Save("Content.zip")
1133        ///   End Using
1134        /// End Sub
1135        /// </code>
1136        /// </example>
1137        public ZipEntry AddEntry(string entryName, string content)
1138        {
1139#if SILVERLIGHT
1140            return AddEntry(entryName, content, System.Text.Encoding.UTF8);
1141#else
1142            return AddEntry(entryName, content, System.Text.Encoding.Default);
1143#endif
1144        }
1145
1146
1147
1148        /// <summary>
1149        ///   Adds a named entry into the zip archive, taking content for the entry
1150        ///   from a string, and using the specified text encoding.
1151        /// </summary>
1152        ///
1153        /// <remarks>
1154        ///
1155        /// <para>
1156        ///   Calling this method creates an entry using the given fileName and
1157        ///   directory path within the archive.  There is no need for a file by the
1158        ///   given name to exist in the filesystem; the name is used within the zip
1159        ///   archive only.
1160        /// </para>
1161        ///
1162        /// <para>
1163        ///   The content for the entry, a string value, is encoded using the given
1164        ///   text encoding. A BOM (byte-order-mark) is emitted into the file, if the
1165        ///   Encoding parameter is set for that.
1166        /// </para>
1167        ///
1168        /// <para>
1169        ///   Most Encoding classes support a constructor that accepts a boolean,
1170        ///   indicating whether to emit a BOM or not. For example see <see
1171        ///   cref="System.Text.UTF8Encoding(bool)"/>.
1172        /// </para>
1173        ///
1174        /// </remarks>
1175        ///
1176        /// <param name="entryName">
1177        ///   The name, including any path, to use within the archive for the entry.
1178        /// </param>
1179        ///
1180        /// <param name="content">
1181        ///   The content of the file, should it be extracted from the zip.
1182        /// </param>
1183        ///
1184        /// <param name="encoding">
1185        ///   The text encoding to use when encoding the string. Be aware: This is
1186        ///   distinct from the text encoding used to encode the fileName, as specified
1187        ///   in <see cref="ProvisionalAlternateEncoding" />.
1188        /// </param>
1189        ///
1190        /// <returns>The <c>ZipEntry</c> added.</returns>
1191        ///
1192        public ZipEntry AddEntry(string entryName, string content, System.Text.Encoding encoding)
1193        {
1194            // cannot employ a using clause here.  We need the stream to
1195            // persist after exit from this method.
1196            var ms = new MemoryStream();
1197
1198            // cannot use a using clause here; StreamWriter takes
1199            // ownership of the stream and Disposes it before we are ready.
1200            var sw = new StreamWriter(ms, encoding);
1201            sw.Write(content);
1202            sw.Flush();
1203
1204            // reset to allow reading later
1205            ms.Seek(0, SeekOrigin.Begin);
1206
1207            return AddEntry(entryName, ms);
1208
1209            // must not dispose the MemoryStream - it will be used later.
1210        }
1211
1212
1213        /// <summary>
1214        ///   Create an entry in the <c>ZipFile</c> using the given <c>Stream</c>
1215        ///   as input.  The entry will have the given filename.
1216        /// </summary>
1217        ///
1218        /// <remarks>
1219        ///
1220        /// <para>
1221        ///   The application should provide an open, readable stream; in this case it
1222        ///   will be read during the call to <see cref="ZipFile.Save()"/> or one of
1223        ///   its overloads.
1224        /// </para>
1225        ///
1226        /// <para>
1227        ///   The passed stream will be read from its current position. If
1228        ///   necessary, callers should set the position in the stream before
1229        ///   calling AddEntry(). This might be appropriate when using this method
1230        ///   with a MemoryStream, for example.
1231        /// </para>
1232        ///
1233        /// <para>
1234        ///   In cases where a large number of streams will be added to the
1235        ///   <c>ZipFile</c>, the application may wish to avoid maintaining all of the
1236        ///   streams open simultaneously.  To handle this situation, the application
1237        ///   should use the <see cref="AddEntry(string, OpenDelegate, CloseDelegate)"/>
1238        ///   overload.
1239        /// </para>
1240        ///
1241        /// <para>
1242        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1243        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1244        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1245        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1246        ///   respective values at the time of this call will be applied to the
1247        ///   <c>ZipEntry</c> added.
1248        /// </para>
1249        ///
1250        /// </remarks>
1251        ///
1252        /// <example>
1253        /// <para>
1254        ///   This example adds a single entry to a <c>ZipFile</c> via a <c>Stream</c>.
1255        /// </para>
1256        ///
1257        /// <code lang="C#">
1258        /// String zipToCreate = "Content.zip";
1259        /// String fileNameInArchive = "Content-From-Stream.bin";
1260        /// using (System.IO.Stream streamToRead = MyStreamOpener())
1261        /// {
1262        ///   using (ZipFile zip = new ZipFile())
1263        ///   {
1264        ///     ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead);
1265        ///     zip.AddFile("Readme.txt");
1266        ///     zip.Save(zipToCreate);  // the stream is read implicitly here
1267        ///   }
1268        /// }
1269        /// </code>
1270        ///
1271        /// <code lang="VB">
1272        /// Dim zipToCreate As String = "Content.zip"
1273        /// Dim fileNameInArchive As String = "Content-From-Stream.bin"
1274        /// Using streamToRead as System.IO.Stream = MyStreamOpener()
1275        ///   Using zip As ZipFile = New ZipFile()
1276        ///     Dim entry as ZipEntry = zip.AddEntry(fileNameInArchive, streamToRead)
1277        ///     zip.AddFile("Readme.txt")
1278        ///     zip.Save(zipToCreate)  '' the stream is read implicitly, here
1279        ///   End Using
1280        /// End Using
1281        /// </code>
1282        /// </example>
1283        ///
1284        /// <seealso cref="Ionic.Zip.ZipFile.UpdateEntry(string, System.IO.Stream)"/>
1285        ///
1286        /// <param name="entryName">
1287        ///   The name, including any path, which is shown in the zip file for the added
1288        ///   entry.
1289        /// </param>
1290        /// <param name="stream">
1291        ///   The input stream from which to grab content for the file
1292        /// </param>
1293        /// <returns>The <c>ZipEntry</c> added.</returns>
1294        public ZipEntry AddEntry(string entryName, Stream stream)
1295        {
1296            ZipEntry ze = ZipEntry.CreateForStream(entryName, stream);
1297            ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
1298            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
1299            return _InternalAddEntry(ze);
1300        }
1301
1302
1303
1304        /// <summary>
1305        ///   Add a ZipEntry for which content is written directly by the application.
1306        /// </summary>
1307        ///
1308        /// <remarks>
1309        /// <para>
1310        ///   When the application needs to write the zip entry data, use this
1311        ///   method to add the ZipEntry. For example, in the case that the
1312        ///   application wishes to write the XML representation of a DataSet into
1313        ///   a ZipEntry, the application can use this method to do so.
1314        /// </para>
1315        ///
1316        /// <para>
1317        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1318        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1319        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1320        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1321        ///   respective values at the time of this call will be applied to the
1322        ///   <c>ZipEntry</c> added.
1323        /// </para>
1324        ///
1325        /// <para>
1326        ///   About progress events: When using the WriteDelegate, DotNetZip does
1327        ///   not issue any SaveProgress events with <c>EventType</c> = <see
1328        ///   cref="ZipProgressEventType.Saving_EntryBytesRead">
1329        ///   Saving_EntryBytesRead</see>. (This is because it is the
1330        ///   application's code that runs in WriteDelegate - there's no way for
1331        ///   DotNetZip to know when to issue a EntryBytesRead event.)
1332        ///   Applications that want to update a progress bar or similar status
1333        ///   indicator should do so from within the WriteDelegate
1334        ///   itself. DotNetZip will issue the other SaveProgress events,
1335        ///   including <see cref="ZipProgressEventType.Saving_Started">
1336        ///   Saving_Started</see>,
1337        ///   <see cref="ZipProgressEventType.Saving_BeforeWriteEntry">
1338        ///   Saving_BeforeWriteEntry</see>, and <see
1339        ///   cref="ZipProgressEventType.Saving_AfterWriteEntry">
1340        ///   Saving_AfterWriteEntry</see>.
1341        /// </para>
1342        ///
1343        /// <para>
1344        ///   Note: When you use PKZip encryption, it's normally necessary to
1345        ///   compute the CRC of the content to be encrypted, before compressing or
1346        ///   encrypting it. Therefore, when using PKZip encryption with a
1347        ///   WriteDelegate, the WriteDelegate CAN BE called twice: once to compute
1348        ///   the CRC, and the second time to potentially compress and
1349        ///   encrypt. Surprising, but true. This is because PKWARE specified that
1350        ///   the encryption initialization data depends on the CRC.
1351        ///   If this happens, for each call of the delegate, your
1352        ///   application must stream the same entry data in its entirety. If your
1353        ///   application writes different data during the second call, it will
1354        ///   result in a corrupt zip file.
1355        /// </para>
1356        ///
1357        /// <para>
1358        ///   The double-read behavior happens with all types of entries, not only
1359        ///   those that use WriteDelegate. It happens if you add an entry from a
1360        ///   filesystem file, or using a string, or a stream, or an opener/closer
1361        ///   pair. But in those cases, DotNetZip takes care of reading twice; in
1362        ///   the case of the WriteDelegate, the application code gets invoked
1363        ///   twice. Be aware.
1364        /// </para>
1365        ///
1366        /// <para>
1367        ///   As you can imagine, this can cause performance problems for large
1368        ///   streams, and it can lead to correctness problems when you use a
1369        ///   <c>WriteDelegate</c>. This is a pretty big pitfall.  There are two
1370        ///   ways to avoid it.  First, and most preferred: don't use PKZIP
1371        ///   encryption.  If you use the WinZip AES encryption, this problem
1372        ///   doesn't occur, because the encryption protocol doesn't require the CRC
1373        ///   up front. Second: if you do choose to use PKZIP encryption, write out
1374        ///   to a non-seekable stream (like standard output, or the
1375        ///   Response.OutputStream in an ASP.NET application).  In this case,
1376        ///   DotNetZip will use an alternative encryption protocol that does not
1377        ///   rely on the CRC of the content.  This also implies setting bit 3 in
1378        ///   the zip entry, which still presents problems for some zip tools.
1379        /// </para>
1380        ///
1381        /// <para>
1382        ///   In the future I may modify DotNetZip to *always* use bit 3 when PKZIP
1383        ///   encryption is in use.  This seems like a win overall, but there will
1384        ///   be some work involved.  If you feel strongly about it, visit the
1385        ///   DotNetZip forums and vote up <see
1386        ///   href="http://dotnetzip.codeplex.com/workitem/13686">the Workitem
1387        ///   tracking this issue</see>.
1388        /// </para>
1389        ///
1390        /// </remarks>
1391        ///
1392        /// <param name="entryName">the name of the entry to add</param>
1393        /// <param name="writer">the delegate which will write the entry content</param>
1394        /// <returns>the ZipEntry added</returns>
1395        ///
1396        /// <example>
1397        ///
1398        ///   This example shows an application filling a DataSet, then saving the
1399        ///   contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an
1400        ///   anonymous delegate in C#. The DataSet XML is never saved to a disk file.
1401        ///
1402        /// <code lang="C#">
1403        /// var c1= new System.Data.SqlClient.SqlConnection(connstring1);
1404        /// var da = new System.Data.SqlClient.SqlDataAdapter()
1405        ///     {
1406        ///         SelectCommand=  new System.Data.SqlClient.SqlCommand(strSelect, c1)
1407        ///     };
1408        ///
1409        /// DataSet ds1 = new DataSet();
1410        /// da.Fill(ds1, "Invoices");
1411        ///
1412        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
1413        /// {
1414        ///     zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) );
1415        ///     zip.Save(zipFileName);
1416        /// }
1417        /// </code>
1418        /// </example>
1419        ///
1420        /// <example>
1421        ///
1422        /// This example uses an anonymous method in C# as the WriteDelegate to provide
1423        /// the data for the ZipEntry. The example is a bit contrived - the
1424        /// <c>AddFile()</c> method is a simpler way to insert the contents of a file
1425        /// into an entry in a zip file. On the other hand, if there is some sort of
1426        /// processing or transformation of the file contents required before writing,
1427        /// the application could use the <c>WriteDelegate</c> to do it, in this way.
1428        ///
1429        /// <code lang="C#">
1430        /// using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ))
1431        /// {
1432        ///     using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
1433        ///     {
1434        ///         zip.AddEntry(zipEntryName, (name,output) =>
1435        ///             {
1436        ///                 byte[] buffer = new byte[BufferSize];
1437        ///                 int n;
1438        ///                 while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
1439        ///                 {
1440        ///                     // could transform the data here...
1441        ///                     output.Write(buffer, 0, n);
1442        ///                     // could update a progress bar here
1443        ///                 }
1444        ///             });
1445        ///
1446        ///         zip.Save(zipFileName);
1447        ///     }
1448        /// }
1449        /// </code>
1450        /// </example>
1451        ///
1452        /// <example>
1453        ///
1454        /// This example uses a named delegate in VB to write data for the given
1455        /// ZipEntry (VB9 does not have anonymous delegates). The example here is a bit
1456        /// contrived - a simpler way to add the contents of a file to a ZipEntry is to
1457        /// simply use the appropriate <c>AddFile()</c> method.  The key scenario for
1458        /// which the <c>WriteDelegate</c> makes sense is saving a DataSet, in XML
1459        /// format, to the zip file. The DataSet can write XML to a stream, and the
1460        /// WriteDelegate is the perfect place to write into the zip file.  There may be
1461        /// other data structures that can write to a stream, but cannot be read as a
1462        /// stream.  The <c>WriteDelegate</c> would be appropriate for those cases as
1463        /// well.
1464        ///
1465        /// <code lang="VB">
1466        /// Private Sub WriteEntry (ByVal name As String, ByVal output As Stream)
1467        ///     Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
1468        ///         Dim n As Integer = -1
1469        ///         Dim buffer As Byte() = New Byte(BufferSize){}
1470        ///         Do While n &lt;&gt; 0
1471        ///             n = input.Read(buffer, 0, buffer.Length)
1472        ///             output.Write(buffer, 0, n)
1473        ///         Loop
1474        ///     End Using
1475        /// End Sub
1476        ///
1477        /// Public Sub Run()
1478        ///     Using zip = New ZipFile
1479        ///         zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
1480        ///         zip.Save(zipFileName)
1481        ///     End Using
1482        /// End Sub
1483        /// </code>
1484        /// </example>
1485        public ZipEntry AddEntry(string entryName, WriteDelegate writer)
1486        {
1487            ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer);
1488            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
1489            return _InternalAddEntry(ze);
1490        }
1491
1492
1493        /// <summary>
1494        ///   Add an entry, for which the application will provide a stream
1495        ///   containing the entry data, on a just-in-time basis.
1496        /// </summary>
1497        ///
1498        /// <remarks>
1499        /// <para>
1500        ///   In cases where the application wishes to open the stream that
1501        ///   holds the content for the ZipEntry, on a just-in-time basis, the
1502        ///   application can use this method.  The application provides an
1503        ///   opener delegate that will be called by the DotNetZip library to
1504        ///   obtain a readable stream that can be read to get the bytes for
1505        ///   the given entry.  Typically, this delegate opens a stream.
1506        ///   Optionally, the application can provide a closer delegate as
1507        ///   well, which will be called by DotNetZip when all bytes have been
1508        ///   read from the entry.
1509        /// </para>
1510        ///
1511        /// <para>
1512        ///   These delegates are called from within the scope of the call to
1513        ///   ZipFile.Save().
1514        /// </para>
1515        ///
1516        /// <para>
1517        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1518        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1519        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1520        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1521        ///   respective values at the time of this call will be applied to the
1522        ///   <c>ZipEntry</c> added.
1523        /// </para>
1524        ///
1525        /// </remarks>
1526        ///
1527        /// <example>
1528        ///
1529        ///   This example uses anonymous methods in C# to open and close the
1530        ///   source stream for the content for a zip entry.
1531        ///
1532        /// <code lang="C#">
1533        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
1534        /// {
1535        ///     zip.AddEntry(zipEntryName,
1536        ///                  (name) =>  File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ),
1537        ///                  (name, stream) =>  stream.Close()
1538        ///                  );
1539        ///
1540        ///     zip.Save(zipFileName);
1541        /// }
1542        /// </code>
1543        ///
1544        /// </example>
1545        ///
1546        /// <example>
1547        ///
1548        ///   This example uses delegates in VB.NET to open and close the
1549        ///   the source stream for the content for a zip entry.  VB 9.0 lacks
1550        ///   support for "Sub" lambda expressions, and so the CloseDelegate must
1551        ///   be an actual, named Sub.
1552        ///
1553        /// <code lang="VB">
1554        ///
1555        /// Function MyStreamOpener(ByVal entryName As String) As Stream
1556        ///     '' This simply opens a file.  You probably want to do somethinig
1557        ///     '' more involved here: open a stream to read from a database,
1558        ///     '' open a stream on an HTTP connection, and so on.
1559        ///     Return File.OpenRead(entryName)
1560        /// End Function
1561        ///
1562        /// Sub MyStreamCloser(entryName As String, stream As Stream)
1563        ///     stream.Close()
1564        /// End Sub
1565        ///
1566        /// Public Sub Run()
1567        ///     Dim dirToZip As String = "fodder"
1568        ///     Dim zipFileToCreate As String = "Archive.zip"
1569        ///     Dim opener As OpenDelegate = AddressOf MyStreamOpener
1570        ///     Dim closer As CloseDelegate = AddressOf MyStreamCloser
1571        ///     Dim numFilestoAdd As Int32 = 4
1572        ///     Using zip As ZipFile = New ZipFile
1573        ///         Dim i As Integer
1574        ///         For i = 0 To numFilesToAdd - 1
1575        ///             zip.AddEntry(String.Format("content-{0:000}.txt"), opener, closer)
1576        ///         Next i
1577        ///         zip.Save(zipFileToCreate)
1578        ///     End Using
1579        /// End Sub
1580        ///
1581        /// </code>
1582        /// </example>
1583        ///
1584        /// <param name="entryName">the name of the entry to add</param>
1585        /// <param name="opener">
1586        ///  the delegate that will be invoked by ZipFile.Save() to get the
1587        ///  readable stream for the given entry. ZipFile.Save() will call
1588        ///  read on this stream to obtain the data for the entry. This data
1589        ///  will then be compressed and written to the newly created zip
1590        ///  file.
1591        /// </param>
1592        /// <param name="closer">
1593        ///  the delegate that will be invoked to close the stream. This may
1594        ///  be null (Nothing in VB), in which case no call is makde to close
1595        ///  the stream.
1596        /// </param>
1597        /// <returns>the ZipEntry added</returns>
1598        ///
1599        public ZipEntry AddEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
1600        {
1601            ZipEntry ze = ZipEntry.CreateForJitStreamProvider(entryName, opener, closer);
1602            ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
1603            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
1604            return _InternalAddEntry(ze);
1605        }
1606
1607
1608
1609        private ZipEntry _InternalAddEntry(ZipEntry ze)
1610        {
1611            // stamp all the props onto the entry
1612            ze._container = new ZipContainer(this);
1613            ze.CompressionMethod = this.CompressionMethod;
1614            ze.CompressionLevel = this.CompressionLevel;
1615            ze.ExtractExistingFile = this.ExtractExistingFile;
1616            ze.ZipErrorAction = this.ZipErrorAction;
1617            ze.SetCompression = this.SetCompression;
1618            ze.AlternateEncoding = this.AlternateEncoding;
1619            ze.AlternateEncodingUsage = this.AlternateEncodingUsage;
1620            ze.Password = this._Password;
1621            ze.Encryption = this.Encryption;
1622            ze.EmitTimesInWindowsFormatWhenSaving = this._emitNtfsTimes;
1623            ze.EmitTimesInUnixFormatWhenSaving = this._emitUnixTimes;
1624            //string key = DictionaryKeyForEntry(ze);
1625            InternalAddEntry(ze.FileName,ze);
1626            AfterAddEntry(ze);
1627            return ze;
1628        }
1629
1630
1631
1632
1633        /// <summary>
1634        ///   Updates the given entry in the <c>ZipFile</c>, using the given
1635        ///   string as content for the <c>ZipEntry</c>.
1636        /// </summary>
1637        ///
1638        /// <remarks>
1639        ///
1640        /// <para>
1641        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for
1642        ///   the given file name and directory path, if it exists, and then calling
1643        ///   <see cref="AddEntry(String,String)" />.  See the documentation for
1644        ///   that method for further explanation. The string content is encoded
1645        ///   using the default encoding for the machine, or on Silverlight, using
1646        ///   UTF-8. This encoding is distinct from the encoding used for the
1647        ///   filename itself.  See <see cref="AlternateEncoding"/>.
1648        /// </para>
1649        ///
1650        /// </remarks>
1651        ///
1652        /// <param name="entryName">
1653        ///   The name, including any path, to use within the archive for the entry.
1654        /// </param>
1655        ///
1656        /// <param name="content">
1657        ///   The content of the file, should it be extracted from the zip.
1658        /// </param>
1659        ///
1660        /// <returns>The <c>ZipEntry</c> added.</returns>
1661        ///
1662        public ZipEntry UpdateEntry(string entryName, string content)
1663        {
1664#if SILVERLIGHT
1665            return UpdateEntry(entryName, content, System.Text.Encoding.UTF8);
1666#else
1667            return UpdateEntry(entryName, content, System.Text.Encoding.Default);
1668#endif
1669        }
1670
1671
1672        /// <summary>
1673        ///   Updates the given entry in the <c>ZipFile</c>, using the given string as
1674        ///   content for the <c>ZipEntry</c>.
1675        /// </summary>
1676        ///
1677        /// <remarks>
1678        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
1679        ///   given file name and directory path, if it exists, and then calling <see
1680        ///   cref="AddEntry(String,String, System.Text.Encoding)" />.  See the
1681        ///   documentation for that method for further explanation.
1682        /// </remarks>
1683        ///
1684        /// <param name="entryName">
1685        ///   The name, including any path, to use within the archive for the entry.
1686        /// </param>
1687        ///
1688        /// <param name="content">
1689        ///   The content of the file, should it be extracted from the zip.
1690        /// </param>
1691        ///
1692        /// <param name="encoding">
1693        ///   The text encoding to use when encoding the string. Be aware: This is
1694        ///   distinct from the text encoding used to encode the filename. See <see
1695        ///   cref="AlternateEncoding" />.
1696        /// </param>
1697        ///
1698        /// <returns>The <c>ZipEntry</c> added.</returns>
1699        ///
1700        public ZipEntry UpdateEntry(string entryName, string content, System.Text.Encoding encoding)
1701        {
1702            RemoveEntryForUpdate(entryName);
1703            return AddEntry(entryName, content, encoding);
1704        }
1705
1706
1707
1708        /// <summary>
1709        ///   Updates the given entry in the <c>ZipFile</c>, using the given delegate
1710        ///   as the source for content for the <c>ZipEntry</c>.
1711        /// </summary>
1712        ///
1713        /// <remarks>
1714        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
1715        ///   given file name and directory path, if it exists, and then calling <see
1716        ///   cref="AddEntry(String,WriteDelegate)" />.  See the
1717        ///   documentation for that method for further explanation.
1718        /// </remarks>
1719        ///
1720        /// <param name="entryName">
1721        ///   The name, including any path, to use within the archive for the entry.
1722        /// </param>
1723        ///
1724        /// <param name="writer">the delegate which will write the entry content.</param>
1725        ///
1726        /// <returns>The <c>ZipEntry</c> added.</returns>
1727        ///
1728        public ZipEntry UpdateEntry(string entryName, WriteDelegate writer)
1729        {
1730            RemoveEntryForUpdate(entryName);
1731            return AddEntry(entryName, writer);
1732        }
1733
1734
1735
1736        /// <summary>
1737        ///   Updates the given entry in the <c>ZipFile</c>, using the given delegates
1738        ///   to open and close the stream that provides the content for the <c>ZipEntry</c>.
1739        /// </summary>
1740        ///
1741        /// <remarks>
1742        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
1743        ///   given file name and directory path, if it exists, and then calling <see
1744        ///   cref="AddEntry(String,OpenDelegate, CloseDelegate)" />.  See the
1745        ///   documentation for that method for further explanation.
1746        /// </remarks>
1747        ///
1748        /// <param name="entryName">
1749        ///   The name, including any path, to use within the archive for the entry.
1750        /// </param>
1751        ///
1752        /// <param name="opener">
1753        ///  the delegate that will be invoked to open the stream
1754        /// </param>
1755        /// <param name="closer">
1756        ///  the delegate that will be invoked to close the stream
1757        /// </param>
1758        ///
1759        /// <returns>The <c>ZipEntry</c> added or updated.</returns>
1760        ///
1761        public ZipEntry UpdateEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
1762        {
1763            RemoveEntryForUpdate(entryName);
1764            return AddEntry(entryName, opener, closer);
1765        }
1766
1767
1768        /// <summary>
1769        ///   Updates the given entry in the <c>ZipFile</c>, using the given stream as
1770        ///   input, and the given filename and given directory Path.
1771        /// </summary>
1772        ///
1773        /// <remarks>
1774        /// <para>
1775        ///   Calling the method is equivalent to calling <c>RemoveEntry()</c> if an
1776        ///   entry by the same name already exists, and then calling <c>AddEntry()</c>
1777        ///   with the given <c>fileName</c> and stream.
1778        /// </para>
1779        ///
1780        /// <para>
1781        ///   The stream must be open and readable during the call to
1782        ///   <c>ZipFile.Save</c>.  You can dispense the stream on a just-in-time basis
1783        ///   using the <see cref="ZipEntry.InputStream"/> property. Check the
1784        ///   documentation of that property for more information.
1785        /// </para>
1786        ///
1787        /// <para>
1788        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1789        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1790        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1791        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1792        ///   respective values at the time of this call will be applied to the
1793        ///   <c>ZipEntry</c> added.
1794        /// </para>
1795        ///
1796        /// </remarks>
1797        ///
1798        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, System.IO.Stream)"/>
1799        /// <seealso cref="ZipEntry.InputStream"/>
1800        ///
1801        /// <param name="entryName">
1802        ///   The name, including any path, to use within the archive for the entry.
1803        /// </param>
1804        ///
1805        /// <param name="stream">The input stream from which to read file data.</param>
1806        /// <returns>The <c>ZipEntry</c> added.</returns>
1807        public ZipEntry UpdateEntry(string entryName, Stream stream)
1808        {
1809            RemoveEntryForUpdate(entryName);
1810            return AddEntry(entryName, stream);
1811        }
1812
1813
1814        private void RemoveEntryForUpdate(string entryName)
1815        {
1816            if (String.IsNullOrEmpty(entryName))
1817                throw new ArgumentNullException("entryName");
1818
1819            string directoryPathInArchive = null;
1820            if (entryName.IndexOf('\\') != -1)
1821            {
1822                directoryPathInArchive = Path.GetDirectoryName(entryName);
1823                entryName = Path.GetFileName(entryName);
1824            }
1825            var key = ZipEntry.NameInArchive(entryName, directoryPathInArchive);
1826            if (this[key] != null)
1827                this.RemoveEntry(key);
1828        }
1829
1830
1831
1832
1833        /// <summary>
1834        ///   Add an entry into the zip archive using the given filename and
1835        ///   directory path within the archive, and the given content for the
1836        ///   file. No file is created in the filesystem.
1837        /// </summary>
1838        ///
1839        /// <param name="byteContent">The data to use for the entry.</param>
1840        ///
1841        /// <param name="entryName">
1842        ///   The name, including any path, to use within the archive for the entry.
1843        /// </param>
1844        ///
1845        /// <returns>The <c>ZipEntry</c> added.</returns>
1846        public ZipEntry AddEntry(string entryName, byte[] byteContent)
1847        {
1848            if (byteContent == null) throw new ArgumentException("bad argument", "byteContent");
1849            var ms = new MemoryStream(byteContent);
1850            return AddEntry(entryName, ms);
1851        }
1852
1853
1854        /// <summary>
1855        ///   Updates the given entry in the <c>ZipFile</c>, using the given byte
1856        ///   array as content for the entry.
1857        /// </summary>
1858        ///
1859        /// <remarks>
1860        ///   Calling this method is equivalent to removing the <c>ZipEntry</c>
1861        ///   for the given filename and directory path, if it exists, and then
1862        ///   calling <see cref="AddEntry(String,byte[])" />.  See the
1863        ///   documentation for that method for further explanation.
1864        /// </remarks>
1865        ///
1866        /// <param name="entryName">
1867        ///   The name, including any path, to use within the archive for the entry.
1868        /// </param>
1869        ///
1870        /// <param name="byteContent">The content to use for the <c>ZipEntry</c>.</param>
1871        ///
1872        /// <returns>The <c>ZipEntry</c> added.</returns>
1873        ///
1874        public ZipEntry UpdateEntry(string entryName, byte[] byteContent)
1875        {
1876            RemoveEntryForUpdate(entryName);
1877            return AddEntry(entryName, byteContent);
1878        }
1879
1880
1881//         private string DictionaryKeyForEntry(ZipEntry ze1)
1882//         {
1883//             var filename = SharedUtilities.NormalizePathForUseInZipFile(ze1.FileName);
1884//             return filename;
1885//         }
1886
1887
1888        /// <summary>
1889        ///   Adds the contents of a filesystem directory to a Zip file archive.
1890        /// </summary>
1891        ///
1892        /// <remarks>
1893        ///
1894        /// <para>
1895        ///   The name of the directory may be a relative path or a fully-qualified
1896        ///   path. Any files within the named directory are added to the archive.  Any
1897        ///   subdirectories within the named directory are also added to the archive,
1898        ///   recursively.
1899        /// </para>
1900        ///
1901        /// <para>
1902        ///   Top-level entries in the named directory will appear as top-level entries
1903        ///   in the zip archive.  Entries in subdirectories in the named directory will
1904        ///   result in entries in subdirectories in the zip archive.
1905        /// </para>
1906        ///
1907        /// <para>
1908        ///   If you want the entries to appear in a containing directory in the zip
1909        ///   archive itself, then you should call the AddDirectory() overload that
1910        ///   allows you to explicitly specify a directory path for use in the archive.
1911        /// </para>
1912        ///
1913        /// <para>
1914        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1915        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1916        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1917        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1918        ///   respective values at the time of this call will be applied to each
1919        ///   ZipEntry added.
1920        /// </para>
1921        ///
1922        /// </remarks>
1923        ///
1924        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
1925        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
1926        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
1927        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
1928        ///
1929        /// <overloads>This method has 2 overloads.</overloads>
1930        ///
1931        /// <param name="directoryName">The name of the directory to add.</param>
1932        /// <returns>The <c>ZipEntry</c> added.</returns>
1933        public ZipEntry AddDirectory(string directoryName)
1934        {
1935            return AddDirectory(directoryName, null);
1936        }
1937
1938
1939        /// <summary>
1940        ///   Adds the contents of a filesystem directory to a Zip file archive,
1941        ///   overriding the path to be used for entries in the archive.
1942        /// </summary>
1943        ///
1944        /// <remarks>
1945        /// <para>
1946        ///   The name of the directory may be a relative path or a fully-qualified
1947        ///   path. The add operation is recursive, so that any files or subdirectories
1948        ///   within the name directory are also added to the archive.
1949        /// </para>
1950        ///
1951        /// <para>
1952        ///   Top-level entries in the named directory will appear as top-level entries
1953        ///   in the zip archive.  Entries in subdirectories in the named directory will
1954        ///   result in entries in subdirectories in the zip archive.
1955        /// </para>
1956        ///
1957        /// <para>
1958        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
1959        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
1960        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
1961        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
1962        ///   respective values at the time of this call will be applied to each
1963        ///   ZipEntry added.
1964        /// </para>
1965        ///
1966        /// </remarks>
1967        ///
1968        /// <example>
1969        /// <para>
1970        ///   In this code, calling the ZipUp() method with a value of "c:\reports" for
1971        ///   the directory parameter will result in a zip file structure in which all
1972        ///   entries are contained in a toplevel "reports" directory.
1973        /// </para>
1974        ///
1975        /// <code lang="C#">
1976        /// public void ZipUp(string targetZip, string directory)
1977        /// {
1978        ///   using (var zip = new ZipFile())
1979        ///   {
1980        ///     zip.AddDirectory(directory, System.IO.Path.GetFileName(directory));
1981        ///     zip.Save(targetZip);
1982        ///   }
1983        /// }
1984        /// </code>
1985        /// </example>
1986        ///
1987        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
1988        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
1989        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
1990        ///
1991        /// <param name="directoryName">The name of the directory to add.</param>
1992        ///
1993        /// <param name="directoryPathInArchive">
1994        ///   Specifies a directory path to use to override any path in the
1995        ///   DirectoryName.  This path may, or may not, correspond to a real directory
1996        ///   in the current filesystem.  If the zip is later extracted, this is the
1997        ///   path used for the extracted file or directory.  Passing <c>null</c>
1998        ///   (<c>Nothing</c> in VB) or the empty string ("") will insert the items at
1999        ///   the root path within the archive.
2000        /// </param>
2001        ///
2002        /// <returns>The <c>ZipEntry</c> added.</returns>
2003        public ZipEntry AddDirectory(string directoryName, string directoryPathInArchive)
2004        {
2005            return AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOnly);
2006        }
2007
2008
2009        /// <summary>
2010        ///   Creates a directory in the zip archive.
2011        /// </summary>
2012        ///
2013        /// <remarks>
2014        ///
2015        /// <para>
2016        ///   Use this when you want to create a directory in the archive but there is
2017        ///   no corresponding filesystem representation for that directory.
2018        /// </para>
2019        ///
2020        /// <para>
2021        ///   You will probably not need to do this in your code. One of the only times
2022        ///   you will want to do this is if you want an empty directory in the zip
2023        ///   archive.  The reason: if you add a file to a zip archive that is stored
2024        ///   within a multi-level directory, all of the directory tree is implicitly
2025        ///   created in the zip archive.
2026        /// </para>
2027        ///
2028        /// </remarks>
2029        ///
2030        /// <param name="directoryNameInArchive">
2031        ///   The name of the directory to create in the archive.
2032        /// </param>
2033        /// <returns>The <c>ZipEntry</c> added.</returns>
2034        public ZipEntry AddDirectoryByName(string directoryNameInArchive)
2035        {
2036            // workitem 9073
2037            ZipEntry dir = ZipEntry.CreateFromNothing(directoryNameInArchive);
2038            dir._container = new ZipContainer(this);
2039            dir.MarkAsDirectory();
2040            dir.AlternateEncoding = this.AlternateEncoding;  // workitem 8984
2041            dir.AlternateEncodingUsage = this.AlternateEncodingUsage;
2042            dir.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
2043            dir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
2044            dir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;
2045            dir._Source = ZipEntrySource.Stream;
2046            //string key = DictionaryKeyForEntry(dir);
2047            InternalAddEntry(dir.FileName,dir);
2048            AfterAddEntry(dir);
2049            return dir;
2050        }
2051
2052
2053
2054        private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
2055                                                  string rootDirectoryPathInArchive,
2056                                                  AddOrUpdateAction action)
2057        {
2058            if (rootDirectoryPathInArchive == null)
2059            {
2060                rootDirectoryPathInArchive = "";
2061            }
2062
2063            return AddOrUpdateDirectoryImpl(directoryName, rootDirectoryPathInArchive, action, true, 0);
2064        }
2065
2066
2067        internal void InternalAddEntry(String name, ZipEntry entry)
2068        {
2069            _entries.Add(name, entry);
2070            _zipEntriesAsList = null;
2071            _contentsChanged = true;
2072        }
2073
2074
2075
2076        private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
2077                                                  string rootDirectoryPathInArchive,
2078                                                  AddOrUpdateAction action,
2079                                                  bool recurse,
2080                                                  int level)
2081        {
2082            if (Verbose)
2083                StatusMessageTextWriter.WriteLine("{0} {1}...",
2084                                                  (action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating",
2085                                                  directoryName);
2086
2087            if (level == 0)
2088            {
2089                _addOperationCanceled = false;
2090                OnAddStarted();
2091            }
2092
2093            // workitem 13371
2094            if (_addOperationCanceled)
2095                return null;
2096
2097            string dirForEntries = rootDirectoryPathInArchive;
2098            ZipEntry baseDir = null;
2099
2100            if (level > 0)
2101            {
2102                int f = directoryName.Length;
2103                for (int i = level; i > 0; i--)
2104                    f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1);
2105
2106                dirForEntries = directoryName.Substring(f + 1);
2107                dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries);
2108            }
2109
2110            // if not top level, or if the root is non-empty, then explicitly add the directory
2111            if (level > 0 || rootDirectoryPathInArchive != "")
2112            {
2113                baseDir = ZipEntry.CreateFromFile(directoryName, dirForEntries);
2114                baseDir._container = new ZipContainer(this);
2115                baseDir.AlternateEncoding = this.AlternateEncoding;  // workitem 6410
2116                baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage;
2117                baseDir.MarkAsDirectory();
2118                baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
2119                baseDir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;
2120
2121                // add the directory only if it does not exist.
2122                // It's not an error if it already exists.
2123                if (!_entries.ContainsKey(baseDir.FileName))
2124                {
2125                    InternalAddEntry(baseDir.FileName,baseDir);
2126                    AfterAddEntry(baseDir);
2127                }
2128                dirForEntries = baseDir.FileName;
2129            }
2130
2131            if (!_addOperationCanceled)
2132            {
2133
2134                String[] filenames = Directory.GetFiles(directoryName);
2135
2136                if (recurse)
2137                {
2138                    // add the files:
2139                    foreach (String filename in filenames)
2140                    {
2141                        if (_addOperationCanceled) break;
2142                        if (action == AddOrUpdateAction.AddOnly)
2143                            AddFile(filename, dirForEntries);
2144                        else
2145                            UpdateFile(filename, dirForEntries);
2146                    }
2147
2148                    if (!_addOperationCanceled)
2149                    {
2150                        // add the subdirectories:
2151                        String[] dirnames = Directory.GetDirectories(directoryName);
2152                        foreach (String dir in dirnames)
2153                        {
2154                            // workitem 8617: Optionally traverse reparse points
2155#if SILVERLIGHT
2156#elif NETCF
2157                            FileAttributes fileAttrs = (FileAttributes) NetCfFile.GetAttributes(dir);
2158#else
2159                            FileAttributes fileAttrs = System.IO.File.GetAttributes(dir);
2160#endif
2161                            if (this.AddDirectoryWillTraverseReparsePoints
2162#if !SILVERLIGHT
2163                                || ((fileAttrs & FileAttributes.ReparsePoint) == 0)
2164#endif
2165                                )
2166                                AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1);
2167
2168                        }
2169
2170                    }
2171                }
2172            }
2173
2174            if (level == 0)
2175                OnAddCompleted();
2176
2177            return baseDir;
2178        }
2179
2180    }
2181
2182}
Note: See TracBrowser for help on using the repository browser.