/// <summary> /// Reads a zip file archive from the named filesystem file using the /// specified options. /// </summary> /// /// <remarks> /// <para> /// This version of the <c>Read()</c> method allows the caller to pass /// in a <c>TextWriter</c> an <c>Encoding</c>, via an instance of the /// <c>ReadOptions</c> class. The <c>ZipFile</c> is read in using the /// specified encoding for entries where UTF-8 encoding is not /// explicitly specified. /// </para> /// </remarks> /// /// <example> /// /// <para> /// This example shows how to read a zip file using the Big-5 Chinese /// code page (950), and extract each entry in the zip file, while /// sending status messages out to the Console. /// </para> /// /// <para> /// For this code to work as intended, the zipfile must have been /// created using the big5 code page (CP950). This is typical, for /// example, when using WinRar on a machine with CP950 set as the /// default code page. In that case, the names of entries within the /// Zip archive will be stored in that code page, and reading the zip /// archive must be done using that code page. If the application did /// not use the correct code page in ZipFile.Read(), then names of /// entries within the zip archive would not be correctly retrieved. /// </para> /// /// <code lang="C#"> /// string zipToExtract = "MyArchive.zip"; /// string extractDirectory = "extract"; /// var options = new ReadOptions /// { /// StatusMessageWriter = System.Console.Out, /// Encoding = System.Text.Encoding.GetEncoding(950) /// }; /// using (ZipFile zip = ZipFile.Read(zipToExtract, options)) /// { /// foreach (ZipEntry e in zip) /// { /// e.Extract(extractDirectory); /// } /// } /// </code> /// /// /// <code lang="VB"> /// Dim zipToExtract as String = "MyArchive.zip" /// Dim extractDirectory as String = "extract" /// Dim options as New ReadOptions /// options.Encoding = System.Text.Encoding.GetEncoding(950) /// options.StatusMessageWriter = System.Console.Out /// Using zip As ZipFile = ZipFile.Read(zipToExtract, options) /// Dim e As ZipEntry /// For Each e In zip /// e.Extract(extractDirectory) /// Next /// End Using /// </code> /// </example> /// /// /// <example> /// /// <para> /// This example shows how to read a zip file using the default /// code page, to remove entries that have a modified date before a given threshold, /// sending status messages out to a <c>StringWriter</c>. /// </para> /// /// <code lang="C#"> /// var options = new ReadOptions /// { /// StatusMessageWriter = new System.IO.StringWriter() /// }; /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip", options)) /// { /// var Threshold = new DateTime(2007,7,4); /// // We cannot remove the entry from the list, within the context of /// // an enumeration of said list. /// // So we add the doomed entry to a list to be removed later. /// // pass 1: mark the entries for removal /// var MarkedEntries = new System.Collections.Generic.List<ZipEntry>(); /// foreach (ZipEntry e in zip) /// { /// if (e.LastModified < Threshold) /// MarkedEntries.Add(e); /// } /// // pass 2: actually remove the entry. /// foreach (ZipEntry zombie in MarkedEntries) /// zip.RemoveEntry(zombie); /// zip.Comment = "This archive has been updated."; /// zip.Save(); /// } /// // can now use contents of sw, eg store in an audit log /// </code> /// /// <code lang="VB"> /// Dim options as New ReadOptions /// options.StatusMessageWriter = New System.IO.StringWriter /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip", options) /// Dim Threshold As New DateTime(2007, 7, 4) /// ' We cannot remove the entry from the list, within the context of /// ' an enumeration of said list. /// ' So we add the doomed entry to a list to be removed later. /// ' pass 1: mark the entries for removal /// Dim MarkedEntries As New System.Collections.Generic.List(Of ZipEntry) /// Dim e As ZipEntry /// For Each e In zip /// If (e.LastModified < Threshold) Then /// MarkedEntries.Add(e) /// End If /// Next /// ' pass 2: actually remove the entry. /// Dim zombie As ZipEntry /// For Each zombie In MarkedEntries /// zip.RemoveEntry(zombie) /// Next /// zip.Comment = "This archive has been updated." /// zip.Save /// End Using /// ' can now use contents of sw, eg store in an audit log /// </code> /// </example> /// /// <exception cref="System.Exception"> /// Thrown if the zipfile cannot be read. The implementation of /// this method relies on <c>System.IO.File.OpenRead</c>, which /// can throw a variety of exceptions, including specific /// exceptions if a file is not found, an unauthorized access /// exception, exceptions for poorly formatted filenames, and so /// on. /// </exception> /// /// <param name="fileName"> /// The name of the zip archive to open. /// This can be a fully-qualified or relative pathname. /// </param> /// /// <param name="options"> /// The set of options to use when reading the zip file. /// </param> /// /// <returns>The ZipFile instance read from the zip archive.</returns> /// /// <seealso cref="ZipFile.Read(Stream, ReadOptions)"/> /// internal static ZipFile Read(string fileName, ReadOptions options) { Stream stream = null; try { string fullPath = GetFullPath(fileName); var file = FileSystem.Current.GetFileFromPathAsync(fullPath).ExecuteSync(); if (file == null) { throw new FileNotFoundException(string.Format("That file ({0}) does not exist!", fileName)); } // don't close the stream, leave it up to the zip file stream = file.OpenAsync(FileAccess.Read).ExecuteSync(); // handle segmented archives var segmentsManager = new FileSystemZipSegmentedStreamManager(fullPath); var zipFile = ZipFile.Read(stream, segmentsManager, options); zipFile.SetShouldDisposeReadStream(true); zipFile.Name = fullPath; return(zipFile); } catch (Exception e1) { if (stream != null) { stream.Dispose(); } throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } }
/// <summary> /// Reads a zip file archive from the named filesystem file using the /// specified options. /// </summary> /// /// <remarks> /// <para> /// This version of the <c>Read()</c> method allows the caller to pass /// in a <c>TextWriter</c> an <c>Encoding</c>, via an instance of the /// <c>ReadOptions</c> class. The <c>ZipFile</c> is read in using the /// specified encoding for entries where UTF-8 encoding is not /// explicitly specified. /// </para> /// </remarks> /// /// <example> /// /// <para> /// This example shows how to read a zip file using the Big-5 Chinese /// code page (950), and extract each entry in the zip file, while /// sending status messages out to the Console. /// </para> /// /// <para> /// For this code to work as intended, the zipfile must have been /// created using the big5 code page (CP950). This is typical, for /// example, when using WinRar on a machine with CP950 set as the /// default code page. In that case, the names of entries within the /// Zip archive will be stored in that code page, and reading the zip /// archive must be done using that code page. If the application did /// not use the correct code page in ZipFile.Read(), then names of /// entries within the zip archive would not be correctly retrieved. /// </para> /// /// <code lang="C#"> /// string zipToExtract = "MyArchive.zip"; /// string extractDirectory = "extract"; /// var options = new ReadOptions /// { /// StatusMessageWriter = System.Console.Out, /// Encoding = System.Text.Encoding.GetEncoding(950) /// }; /// using (ZipFile zip = ZipFile.Read(zipToExtract, options)) /// { /// foreach (ZipEntry e in zip) /// { /// e.Extract(extractDirectory); /// } /// } /// </code> /// /// /// <code lang="VB"> /// Dim zipToExtract as String = "MyArchive.zip" /// Dim extractDirectory as String = "extract" /// Dim options as New ReadOptions /// options.Encoding = System.Text.Encoding.GetEncoding(950) /// options.StatusMessageWriter = System.Console.Out /// Using zip As ZipFile = ZipFile.Read(zipToExtract, options) /// Dim e As ZipEntry /// For Each e In zip /// e.Extract(extractDirectory) /// Next /// End Using /// </code> /// </example> /// /// /// <example> /// /// <para> /// This example shows how to read a zip file using the default /// code page, to remove entries that have a modified date before a given threshold, /// sending status messages out to a <c>StringWriter</c>. /// </para> /// /// <code lang="C#"> /// var options = new ReadOptions /// { /// StatusMessageWriter = new System.IO.StringWriter() /// }; /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip", options)) /// { /// var Threshold = new DateTime(2007,7,4); /// // We cannot remove the entry from the list, within the context of /// // an enumeration of said list. /// // So we add the doomed entry to a list to be removed later. /// // pass 1: mark the entries for removal /// var MarkedEntries = new System.Collections.Generic.List<ZipEntry>(); /// foreach (ZipEntry e in zip) /// { /// if (e.LastModified < Threshold) /// MarkedEntries.Add(e); /// } /// // pass 2: actually remove the entry. /// foreach (ZipEntry zombie in MarkedEntries) /// zip.RemoveEntry(zombie); /// zip.Comment = "This archive has been updated."; /// zip.Save(); /// } /// // can now use contents of sw, eg store in an audit log /// </code> /// /// <code lang="VB"> /// Dim options as New ReadOptions /// options.StatusMessageWriter = New System.IO.StringWriter /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip", options) /// Dim Threshold As New DateTime(2007, 7, 4) /// ' We cannot remove the entry from the list, within the context of /// ' an enumeration of said list. /// ' So we add the doomed entry to a list to be removed later. /// ' pass 1: mark the entries for removal /// Dim MarkedEntries As New System.Collections.Generic.List(Of ZipEntry) /// Dim e As ZipEntry /// For Each e In zip /// If (e.LastModified < Threshold) Then /// MarkedEntries.Add(e) /// End If /// Next /// ' pass 2: actually remove the entry. /// Dim zombie As ZipEntry /// For Each zombie In MarkedEntries /// zip.RemoveEntry(zombie) /// Next /// zip.Comment = "This archive has been updated." /// zip.Save /// End Using /// ' can now use contents of sw, eg store in an audit log /// </code> /// </example> /// /// <exception cref="System.Exception"> /// Thrown if the zipfile cannot be read. The implementation of /// this method relies on <c>System.IO.File.OpenRead</c>, which /// can throw a variety of exceptions, including specific /// exceptions if a file is not found, an unauthorized access /// exception, exceptions for poorly formatted filenames, and so /// on. /// </exception> /// /// <param name="fileName"> /// The name of the zip archive to open. /// This can be a fully-qualified or relative pathname. /// </param> /// /// <param name="options"> /// The set of options to use when reading the zip file. /// </param> /// /// <returns>The ZipFile instance read from the zip archive.</returns> /// /// <seealso cref="ZipFile.Read(Stream, ReadOptions)"/> /// internal static ZipFile Read(string fileName, ReadOptions options) { Stream stream = null; try { string fullPath = GetFullPath(fileName); var file = FileSystem.Current.GetFileFromPathAsync(fullPath).ExecuteSync(); if (file == null) { throw new FileNotFoundException(string.Format("That file ({0}) does not exist!", fileName)); } // don't close the stream, leave it up to the zip file stream = file.OpenAsync(FileAccess.Read).ExecuteSync(); // handle segmented archives var segmentsManager = new FileSystemZipSegmentedStreamManager(fullPath); var zipFile = ZipFile.Read(stream, segmentsManager, options); zipFile.SetShouldDisposeReadStream(true); zipFile.Name = fullPath; return zipFile; } catch (Exception e1) { if (stream != null) { stream.Dispose(); } throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); } }
/// <summary> /// Save the file to a new zipfile, with the given name. /// </summary> /// /// <remarks> /// <para> /// This method allows the application to explicitly specify the name of the zip /// file when saving. Use this when creating a new zip file, or when /// updating a zip archive. /// </para> /// /// <para> /// An application can also save a zip archive in several places by calling this /// method multiple times in succession, with different filenames. /// </para> /// /// <para> /// The <c>ZipFile</c> instance is written to storage, typically a zip file in a /// filesystem, only when the caller calls <c>Save</c>. The Save operation writes /// the zip content to a temporary file, and then renames the temporary file /// to the desired name. If necessary, this method will delete a pre-existing file /// before the rename. /// </para> /// /// </remarks> /// /// <exception cref="System.ArgumentException"> /// Thrown if you specify a directory for the filename. /// </exception> /// /// <param name="fileName"> /// The name of the zip archive to save to. Existing files will /// be overwritten with great prejudice. /// </param> /// /// <example> /// This example shows how to create and Save a zip file. /// <code> /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(@"c:\reports\January"); /// zip.Save("January.zip"); /// } /// </code> /// /// <code lang="VB"> /// Using zip As New ZipFile() /// zip.AddDirectory("c:\reports\January") /// zip.Save("January.zip") /// End Using /// </code> /// /// </example> /// /// <example> /// This example shows how to update a zip file. /// <code> /// using (ZipFile zip = ZipFile.Read("ExistingArchive.zip")) /// { /// zip.AddFile("NewData.csv"); /// zip.Save("UpdatedArchive.zip"); /// } /// </code> /// /// <code lang="VB"> /// Using zip As ZipFile = ZipFile.Read("ExistingArchive.zip") /// zip.AddFile("NewData.csv") /// zip.Save("UpdatedArchive.zip") /// End Using /// </code> /// /// </example> public static void Save(this ZipFile zipFile, String fileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } string fullPath = GetFullPath(fileName); if (FileSystem.Current.GetFolderFromPathAsync(fullPath).ExecuteSync() != null) { throw new ZipException("Bad Directory", new ArgumentException("That name specifies an existing directory. Please specify a filename.", "fileName")); } var fullDirectoryName = Path.GetDirectoryName(fullPath); var directoryName = Path.GetDirectoryName(fileName); var folder = FileSystem.Current.GetFolderFromPathAsync(fullDirectoryName).ExecuteSync(); if (folder == null) { throw new ZipException("Bad Directory", new ArgumentException(string.Format("That folder ({0}) does not exist!", directoryName))); } // check up here so we may shortcut some IO operations in the future :) var fileExists = folder.CheckExistsAsync(fullPath).ExecuteSync() == ExistenceCheckResult.FileExists; // write to a temporary file so that we can read from the same zip when saving var tmpName = Path.GetRandomFileName(); try { ZipSegmentedStreamManager segmentsManager = null; if (zipFile.MaxOutputSegmentSize != 0) { // save segmented files using the segments manager var manager = new FileSystemZipSegmentedStreamManager(fullPath); segmentsManager = manager; zipFile.Save(segmentsManager); tmpName = manager.TemporaryName; } else { // save var tmpFile = folder.CreateFileAsync(tmpName, CreationCollisionOption.ReplaceExisting).ExecuteSync(); tmpName = tmpFile.Path; using (var tmpStream = tmpFile.OpenAsync(FileAccess.ReadAndWrite).ExecuteSync()) { zipFile.Save(tmpStream); } } // if it wasn't canceled if (!zipFile.IsSaveOperationCanceled()) { // disconnect from the stream zipFile.SetUnderlyingZipStream(null); // move the temporary file into position zipFile.OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive); ZipEntryExtensions.MoveFileInPlace(fileExists, fullPath, tmpName); zipFile.OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive); // and now set the read stream to be that of the new file var targetFile = FileSystem.Current.GetFileFromPathAsync(fullPath).ExecuteSync(); var fileStream = targetFile.OpenAsync(FileAccess.Read).ExecuteSync(); zipFile.SetUnderlyingZipSegmentedStreamManager(segmentsManager); zipFile.SetUnderlyingZipStream(fileStream); zipFile.SetShouldDisposeReadStream(true); zipFile.Name = fullPath; // so we can shortcut the existance checks tmpName = null; } } finally { // An exception has occurred. If the file exists, check // to see if it existed before we tried extracting. If // it did not, attempt to remove the target file. There // is a small possibility that the existing file has // been extracted successfully, overwriting a previously // existing file, and an exception was thrown after that // but before final completion (setting times, etc). In // that case the file will remain, even though some // error occurred. Nothing to be done about it. if (tmpName != null) { var tmpFile = folder.GetFileAsync(tmpName).ExecuteSync(); tmpFile.DeleteAsync(); } } }
/// <summary> /// Save the file to a new zipfile, with the given name. /// </summary> /// /// <remarks> /// <para> /// This method allows the application to explicitly specify the name of the zip /// file when saving. Use this when creating a new zip file, or when /// updating a zip archive. /// </para> /// /// <para> /// An application can also save a zip archive in several places by calling this /// method multiple times in succession, with different filenames. /// </para> /// /// <para> /// The <c>ZipFile</c> instance is written to storage, typically a zip file in a /// filesystem, only when the caller calls <c>Save</c>. The Save operation writes /// the zip content to a temporary file, and then renames the temporary file /// to the desired name. If necessary, this method will delete a pre-existing file /// before the rename. /// </para> /// /// </remarks> /// /// <exception cref="System.ArgumentException"> /// Thrown if you specify a directory for the filename. /// </exception> /// /// <param name="fileName"> /// The name of the zip archive to save to. Existing files will /// be overwritten with great prejudice. /// </param> /// /// <example> /// This example shows how to create and Save a zip file. /// <code> /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(@"c:\reports\January"); /// zip.Save("January.zip"); /// } /// </code> /// /// <code lang="VB"> /// Using zip As New ZipFile() /// zip.AddDirectory("c:\reports\January") /// zip.Save("January.zip") /// End Using /// </code> /// /// </example> /// /// <example> /// This example shows how to update a zip file. /// <code> /// using (ZipFile zip = ZipFile.Read("ExistingArchive.zip")) /// { /// zip.AddFile("NewData.csv"); /// zip.Save("UpdatedArchive.zip"); /// } /// </code> /// /// <code lang="VB"> /// Using zip As ZipFile = ZipFile.Read("ExistingArchive.zip") /// zip.AddFile("NewData.csv") /// zip.Save("UpdatedArchive.zip") /// End Using /// </code> /// /// </example> public static void Save(this ZipFile zipFile, String fileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } string fullPath = GetFullPath(fileName); if (FileSystem.Current.GetFolderFromPathAsync(fullPath).ExecuteSync() != null) throw new ZipException("Bad Directory", new ArgumentException("That name specifies an existing directory. Please specify a filename.", "fileName")); var fullDirectoryName = Path.GetDirectoryName(fullPath); var directoryName = Path.GetDirectoryName(fileName); var folder = FileSystem.Current.GetFolderFromPathAsync(fullDirectoryName).ExecuteSync(); if (folder == null) throw new ZipException("Bad Directory", new ArgumentException(string.Format("That folder ({0}) does not exist!", directoryName))); // check up here so we may shortcut some IO operations in the future :) var fileExists = folder.CheckExistsAsync(fullPath).ExecuteSync() == ExistenceCheckResult.FileExists; // write to a temporary file so that we can read from the same zip when saving var tmpName = Path.GetRandomFileName(); try { ZipSegmentedStreamManager segmentsManager = null; if (zipFile.MaxOutputSegmentSize != 0) { // save segmented files using the segments manager var manager = new FileSystemZipSegmentedStreamManager(fullPath); segmentsManager = manager; zipFile.Save(segmentsManager); tmpName = manager.TemporaryName; } else { // save var tmpFile = folder.CreateFileAsync(tmpName, CreationCollisionOption.ReplaceExisting).ExecuteSync(); tmpName = tmpFile.Path; using (var tmpStream = tmpFile.OpenAsync(FileAccess.ReadAndWrite).ExecuteSync()) { zipFile.Save(tmpStream); } } // if it wasn't canceled if (!zipFile.IsSaveOperationCanceled()) { // disconnect from the stream zipFile.SetUnderlyingZipStream(null); // move the temporary file into position zipFile.OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive); ZipEntryExtensions.MoveFileInPlace(fileExists, fullPath, tmpName); zipFile.OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive); // and now set the read stream to be that of the new file var targetFile = FileSystem.Current.GetFileFromPathAsync(fullPath).ExecuteSync(); var fileStream = targetFile.OpenAsync(FileAccess.Read).ExecuteSync(); zipFile.SetUnderlyingZipSegmentedStreamManager(segmentsManager); zipFile.SetUnderlyingZipStream(fileStream); zipFile.SetShouldDisposeReadStream(true); zipFile.Name = fullPath; // so we can shortcut the existance checks tmpName = null; } } finally { // An exception has occurred. If the file exists, check // to see if it existed before we tried extracting. If // it did not, attempt to remove the target file. There // is a small possibility that the existing file has // been extracted successfully, overwriting a previously // existing file, and an exception was thrown after that // but before final completion (setting times, etc). In // that case the file will remain, even though some // error occurred. Nothing to be done about it. if (tmpName != null) { var tmpFile = folder.GetFileAsync(tmpName).ExecuteSync(); tmpFile.DeleteAsync(); } } }