This repository was archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Refactor, clean up ZipFile assembly #30364
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
307 changes: 16 additions & 291 deletions
307
...File/src/System/IO/Compression/ZipFile.cs → ...c/System/IO/Compression/ZipFile.Create.cs
Large diffs are not rendered by default.
Oops, something went wrong.
197 changes: 197 additions & 0 deletions
197
src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Extract.cs
Large diffs are not rendered by default.
Oops, something went wrong.
80 changes: 80 additions & 0 deletions
80
src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Utils.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Buffers; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace System.IO.Compression | ||
| { | ||
| internal static partial class ZipFileUtils | ||
| { | ||
| // Per the .ZIP File Format Specification 4.4.17.1 all slashes should be forward slashes | ||
| public const char PathSeparator = '/'; | ||
|
|
||
| public static string EntryFromPath(string entry, int offset, int length, ref char[] buffer, bool appendPathSeparator = false) | ||
| { | ||
| Debug.Assert(length <= entry.Length - offset); | ||
| Debug.Assert(buffer != null); | ||
|
|
||
| // Remove any leading slashes from the entry name: | ||
| while (length > 0) | ||
| { | ||
| if (entry[offset] != Path.DirectorySeparatorChar && | ||
| entry[offset] != Path.AltDirectorySeparatorChar) | ||
| break; | ||
|
|
||
| offset++; | ||
| length--; | ||
| } | ||
|
|
||
| if (length == 0) | ||
| return appendPathSeparator ? PathSeparator.ToString() : string.Empty; | ||
|
|
||
| int resultLength = appendPathSeparator ? length + 1 : length; | ||
| EnsureCapacity(ref buffer, resultLength); | ||
| entry.CopyTo(offset, buffer, 0, length); | ||
|
|
||
| // '/' is a more broadly recognized directory separator on all platforms (eg: mac, linux) | ||
| // We don't use Path.DirectorySeparatorChar or AltDirectorySeparatorChar because this is | ||
| // explicitly trying to standardize to '/' | ||
| for (int i = 0; i < length; i++) | ||
| { | ||
| char ch = buffer[i]; | ||
| if (ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar) | ||
| buffer[i] = PathSeparator; | ||
| } | ||
|
|
||
| if (appendPathSeparator) | ||
| buffer[length] = PathSeparator; | ||
|
|
||
| return new string(buffer, 0, resultLength); | ||
| } | ||
|
|
||
| public static void EnsureCapacity(ref char[] buffer, int min) | ||
| { | ||
| Debug.Assert(buffer != null); | ||
| Debug.Assert(min > 0); | ||
|
|
||
| if (buffer.Length < min) | ||
| { | ||
| int newCapacity = buffer.Length * 2; | ||
| if (newCapacity < min) | ||
| newCapacity = min; | ||
| ArrayPool<char>.Shared.Return(buffer); | ||
| buffer = ArrayPool<char>.Shared.Rent(newCapacity); | ||
| } | ||
| } | ||
|
|
||
| public static bool IsDirEmpty(DirectoryInfo possiblyEmptyDir) | ||
| { | ||
| using (IEnumerator<string> enumerator = Directory.EnumerateFileSystemEntries(possiblyEmptyDir.FullName).GetEnumerator()) | ||
| return !enumerator.MoveNext(); | ||
| } | ||
| } | ||
| } |
118 changes: 118 additions & 0 deletions
118
...m.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System.ComponentModel; | ||
|
|
||
| namespace System.IO.Compression | ||
| { | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public static partial class ZipFileExtensions | ||
| { | ||
| /// <summary> | ||
| /// <p>Adds a file from the file system to the archive under the specified entry name. | ||
| /// The new entry in the archive will contain the contents of the file. | ||
| /// The last write time of the archive entry is set to the last write time of the file on the file system. | ||
| /// If an entry with the specified name already exists in the archive, a second entry will be created that has an identical name. | ||
| /// If the specified source file has an invalid last modified time, the first datetime representable in the Zip timestamp format | ||
| /// (midnight on January 1, 1980) will be used.</p> | ||
| /// | ||
| /// <p>If an entry with the specified name already exists in the archive, a second entry will be created that has an identical name.</p> | ||
| /// | ||
| /// <p>Since no <code>CompressionLevel</code> is specified, the default provided by the implementation of the underlying compression | ||
| /// algorithm will be used; the <code>ZipArchive</code> will not impose its own default. | ||
| /// (Currently, the underlying compression algorithm is provided by the <code>System.IO.Compression.DeflateStream</code> class.)</p> | ||
| /// </summary> | ||
| /// | ||
| /// <exception cref="ArgumentException">sourceFileName is a zero-length string, contains only whitespace, or contains one or more | ||
| /// invalid characters as defined by InvalidPathChars. -or- entryName is a zero-length string.</exception> | ||
| /// <exception cref="ArgumentNullException">sourceFileName or entryName is null.</exception> | ||
| /// <exception cref="PathTooLongException">In sourceFileName, the specified path, file name, or both exceed the system-defined maximum length. | ||
| /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.</exception> | ||
| /// <exception cref="DirectoryNotFoundException">The specified sourceFileName is invalid, (for example, it is on an unmapped drive).</exception> | ||
| /// <exception cref="IOException">An I/O error occurred while opening the file specified by sourceFileName.</exception> | ||
| /// <exception cref="UnauthorizedAccessException">sourceFileName specified a directory. -or- The caller does not have the | ||
| /// required permission.</exception> | ||
| /// <exception cref="FileNotFoundException">The file specified in sourceFileName was not found. </exception> | ||
| /// <exception cref="NotSupportedException">sourceFileName is in an invalid format or the ZipArchive does not support writing.</exception> | ||
| /// <exception cref="ObjectDisposedException">The ZipArchive has already been closed.</exception> | ||
| /// | ||
| /// <param name="sourceFileName">The path to the file on the file system to be copied from. The path is permitted to specify | ||
| /// relative or absolute path information. Relative path information is interpreted as relative to the current working directory.</param> | ||
| /// <param name="entryName">The name of the entry to be created.</param> | ||
| /// <returns>A wrapper for the newly created entry.</returns> | ||
| public static ZipArchiveEntry CreateEntryFromFile(this ZipArchive destination, string sourceFileName, string entryName) => | ||
| DoCreateEntryFromFile(destination, sourceFileName, entryName, null); | ||
|
|
||
|
|
||
| /// <summary> | ||
| /// <p>Adds a file from the file system to the archive under the specified entry name. | ||
| /// The new entry in the archive will contain the contents of the file. | ||
| /// The last write time of the archive entry is set to the last write time of the file on the file system. | ||
| /// If an entry with the specified name already exists in the archive, a second entry will be created that has an identical name. | ||
| /// If the specified source file has an invalid last modified time, the first datetime representable in the Zip timestamp format | ||
| /// (midnight on January 1, 1980) will be used.</p> | ||
| /// <p>If an entry with the specified name already exists in the archive, a second entry will be created that has an identical name.</p> | ||
| /// </summary> | ||
| /// <exception cref="ArgumentException">sourceFileName is a zero-length string, contains only whitespace, or contains one or more | ||
| /// invalid characters as defined by InvalidPathChars. -or- entryName is a zero-length string.</exception> | ||
| /// <exception cref="ArgumentNullException">sourceFileName or entryName is null.</exception> | ||
| /// <exception cref="PathTooLongException">In sourceFileName, the specified path, file name, or both exceed the system-defined maximum length. | ||
| /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.</exception> | ||
| /// <exception cref="DirectoryNotFoundException">The specified sourceFileName is invalid, (for example, it is on an unmapped drive).</exception> | ||
| /// <exception cref="IOException">An I/O error occurred while opening the file specified by sourceFileName.</exception> | ||
| /// <exception cref="UnauthorizedAccessException">sourceFileName specified a directory. | ||
| /// -or- The caller does not have the required permission.</exception> | ||
| /// <exception cref="FileNotFoundException">The file specified in sourceFileName was not found. </exception> | ||
| /// <exception cref="NotSupportedException">sourceFileName is in an invalid format or the ZipArchive does not support writing.</exception> | ||
| /// <exception cref="ObjectDisposedException">The ZipArchive has already been closed.</exception> | ||
| /// | ||
| /// <param name="sourceFileName">The path to the file on the file system to be copied from. The path is permitted to specify relative | ||
| /// or absolute path information. Relative path information is interpreted as relative to the current working directory.</param> | ||
| /// <param name="entryName">The name of the entry to be created.</param> | ||
| /// <param name="compressionLevel">The level of the compression (speed/memory vs. compressed size trade-off).</param> | ||
| /// <returns>A wrapper for the newly created entry.</returns> | ||
| public static ZipArchiveEntry CreateEntryFromFile(this ZipArchive destination, | ||
| string sourceFileName, string entryName, CompressionLevel compressionLevel) => | ||
| DoCreateEntryFromFile(destination, sourceFileName, entryName, compressionLevel); | ||
|
|
||
| internal static ZipArchiveEntry DoCreateEntryFromFile(this ZipArchive destination, | ||
| string sourceFileName, string entryName, CompressionLevel? compressionLevel) | ||
| { | ||
| if (destination == null) | ||
|
||
| throw new ArgumentNullException(nameof(destination)); | ||
|
|
||
| if (sourceFileName == null) | ||
| throw new ArgumentNullException(nameof(sourceFileName)); | ||
|
|
||
| if (entryName == null) | ||
| throw new ArgumentNullException(nameof(entryName)); | ||
|
|
||
| // Checking of compressionLevel is passed down to DeflateStream and the IDeflater implementation | ||
| // as it is a pluggable component that completely encapsulates the meaning of compressionLevel. | ||
|
|
||
| // Argument checking gets passed down to FileStream's ctor and CreateEntry | ||
|
|
||
| using (Stream fs = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) | ||
| { | ||
| ZipArchiveEntry entry = compressionLevel.HasValue | ||
| ? destination.CreateEntry(entryName, compressionLevel.Value) | ||
| : destination.CreateEntry(entryName); | ||
|
|
||
| DateTime lastWrite = File.GetLastWriteTime(sourceFileName); | ||
|
|
||
| // If file to be archived has an invalid last modified time, use the first datetime representable in the Zip timestamp format | ||
| // (midnight on January 1, 1980): | ||
| if (lastWrite.Year < 1980 || lastWrite.Year > 2107) | ||
| lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); | ||
|
|
||
| entry.LastWriteTime = lastWrite; | ||
|
|
||
| using (Stream es = entry.Open()) | ||
| fs.CopyTo(es); | ||
|
|
||
| return entry; | ||
| } | ||
| } | ||
| } | ||
| } | ||
82 changes: 82 additions & 0 deletions
82
....IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Extract.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System.ComponentModel; | ||
|
|
||
| namespace System.IO.Compression | ||
| { | ||
| public static partial class ZipFileExtensions | ||
| { | ||
| /// <summary> | ||
| /// Extracts all of the files in the archive to a directory on the file system. The specified directory may already exist. | ||
| /// This method will create all subdirectories and the specified directory if necessary. | ||
| /// If there is an error while extracting the archive, the archive will remain partially extracted. | ||
| /// Each entry will be extracted such that the extracted file has the same relative path to destinationDirectoryName as the | ||
| /// entry has to the root of the archive. If a file to be archived has an invalid last modified time, the first datetime | ||
| /// representable in the Zip timestamp format (midnight on January 1, 1980) will be used. | ||
| /// </summary> | ||
| /// | ||
| /// <exception cref="ArgumentException">destinationDirectoryName is a zero-length string, contains only whitespace, | ||
| /// or contains one or more invalid characters as defined by InvalidPathChars.</exception> | ||
| /// <exception cref="ArgumentNullException">destinationDirectoryName is null.</exception> | ||
| /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. | ||
| /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.</exception> | ||
| /// <exception cref="DirectoryNotFoundException">The specified path is invalid, (for example, it is on an unmapped drive).</exception> | ||
| /// <exception cref="IOException">An archive entry?s name is zero-length, contains only whitespace, or contains one or more invalid | ||
| /// characters as defined by InvalidPathChars. -or- Extracting an archive entry would have resulted in a destination | ||
| /// file that is outside destinationDirectoryName (for example, if the entry name contains parent directory accessors). | ||
| /// -or- An archive entry has the same name as an already extracted entry from the same archive.</exception> | ||
| /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception> | ||
| /// <exception cref="NotSupportedException">destinationDirectoryName is in an invalid format. </exception> | ||
| /// <exception cref="InvalidDataException">An archive entry was not found or was corrupt. | ||
| /// -or- An archive entry has been compressed using a compression method that is not supported.</exception> | ||
| /// | ||
| /// <param name="destinationDirectoryName">The path to the directory on the file system. | ||
| /// The directory specified must not exist. The path is permitted to specify relative or absolute path information. | ||
| /// Relative path information is interpreted as relative to the current working directory.</param> | ||
| public static void ExtractToDirectory(this ZipArchive source, string destinationDirectoryName) => ExtractToDirectory(source, destinationDirectoryName, overwrite: false); | ||
|
|
||
| /// <summary> | ||
| /// Extracts all of the files in the archive to a directory on the file system. The specified directory may already exist. | ||
| /// This method will create all subdirectories and the specified directory if necessary. | ||
| /// If there is an error while extracting the archive, the archive will remain partially extracted. | ||
| /// Each entry will be extracted such that the extracted file has the same relative path to destinationDirectoryName as the | ||
| /// entry has to the root of the archive. If a file to be archived has an invalid last modified time, the first datetime | ||
| /// representable in the Zip timestamp format (midnight on January 1, 1980) will be used. | ||
| /// </summary> | ||
| /// | ||
| /// <exception cref="ArgumentException">destinationDirectoryName is a zero-length string, contains only whitespace, | ||
| /// or contains one or more invalid characters as defined by InvalidPathChars.</exception> | ||
| /// <exception cref="ArgumentNullException">destinationDirectoryName is null.</exception> | ||
| /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. | ||
| /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.</exception> | ||
| /// <exception cref="DirectoryNotFoundException">The specified path is invalid, (for example, it is on an unmapped drive).</exception> | ||
| /// <exception cref="IOException">An archive entry?s name is zero-length, contains only whitespace, or contains one or more invalid | ||
| /// characters as defined by InvalidPathChars. -or- Extracting an archive entry would have resulted in a destination | ||
| /// file that is outside destinationDirectoryName (for example, if the entry name contains parent directory accessors). | ||
| /// -or- An archive entry has the same name as an already extracted entry from the same archive.</exception> | ||
| /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception> | ||
| /// <exception cref="NotSupportedException">destinationDirectoryName is in an invalid format. </exception> | ||
| /// <exception cref="InvalidDataException">An archive entry was not found or was corrupt. | ||
| /// -or- An archive entry has been compressed using a compression method that is not supported.</exception> | ||
| /// | ||
| /// <param name="destinationDirectoryName">The path to the directory on the file system. | ||
| /// The directory specified must not exist. The path is permitted to specify relative or absolute path information. | ||
| /// Relative path information is interpreted as relative to the current working directory.</param> | ||
| /// <param name="overwrite">True to indicate overwrite.</param> | ||
| public static void ExtractToDirectory(this ZipArchive source, string destinationDirectoryName, bool overwrite) | ||
| { | ||
| if (source == null) | ||
| throw new ArgumentNullException(nameof(source)); | ||
|
|
||
| if (destinationDirectoryName == null) | ||
| throw new ArgumentNullException(nameof(destinationDirectoryName)); | ||
|
|
||
| foreach (ZipArchiveEntry entry in source.Entries) | ||
| { | ||
| entry.ExtractRelativeToDirectory(destinationDirectoryName, overwrite); | ||
| } | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and also this line