2 using System.Collections.Generic;
6 using SharpCompress.Common;
7 using SharpCompress.Common.Zip;
8 using SharpCompress.Common.Zip.Headers;
9 using SharpCompress.Compressor.Deflate;
10 using SharpCompress.Reader;
11 using SharpCompress.Reader.Zip;
12 using SharpCompress.Writer.Zip;
14 namespace SharpCompress.Archive.Zip
16 internal class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
18 private readonly SeekableZipHeaderFactory headerFactory;
21 /// Gets or sets the compression level applied to files added to the archive,
22 /// if the compression method is set to deflate
24 public CompressionLevel DeflateCompressionLevel { get; set; }
26 #if !PORTABLE && !NETFX_CORE
28 /// Constructor expects a filepath to an existing file.
30 /// <param name="filePath"></param>
31 /// <param name="password"></param>
32 public static ZipArchive Open(string filePath, string password = null)
34 return Open(filePath, Options.None, password);
38 /// Constructor with a FileInfo object to an existing file.
40 /// <param name="fileInfo"></param>
41 /// <param name="password"></param>
42 public static ZipArchive Open(FileInfo fileInfo, string password = null)
44 return Open(fileInfo, Options.None, password);
48 /// Constructor expects a filepath to an existing file.
50 /// <param name="filePath"></param>
51 /// <param name="options"></param>
52 /// <param name="password"></param>
53 public static ZipArchive Open(string filePath, Options options, string password = null)
55 filePath.CheckNotNullOrEmpty("filePath");
56 return Open(new FileInfo(filePath), options, password);
60 /// Constructor with a FileInfo object to an existing file.
62 /// <param name="fileInfo"></param>
63 /// <param name="options"></param>
64 /// <param name="password"></param>
65 public static ZipArchive Open(FileInfo fileInfo, Options options, string password = null)
67 fileInfo.CheckNotNull("fileInfo");
68 return new ZipArchive(fileInfo, options, password);
73 /// Takes a seekable Stream as a source
75 /// <param name="stream"></param>
76 /// <param name="password"></param>
77 public static ZipArchive Open(Stream stream, string password = null)
79 stream.CheckNotNull("stream");
80 return Open(stream, Options.None, password);
84 /// Takes a seekable Stream as a source
86 /// <param name="stream"></param>
87 /// <param name="options"></param>
88 /// <param name="password"></param>
89 public static ZipArchive Open(Stream stream, Options options, string password = null)
91 stream.CheckNotNull("stream");
92 return new ZipArchive(stream, options, password);
95 #if !PORTABLE && !NETFX_CORE
96 public static bool IsZipFile(string filePath, string password = null)
98 return IsZipFile(new FileInfo(filePath), password);
101 public static bool IsZipFile(FileInfo fileInfo, string password = null)
103 if (!fileInfo.Exists)
107 using (Stream stream = fileInfo.OpenRead())
109 return IsZipFile(stream, password);
114 public static bool IsZipFile(Stream stream, string password = null)
116 StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory(password);
120 headerFactory.ReadStreamHeader(stream).FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
125 return Enum.IsDefined(typeof (ZipHeaderType), header.ZipHeaderType);
127 catch (CryptographicException)
137 #if !PORTABLE && !NETFX_CORE
139 /// Constructor with a FileInfo object to an existing file.
141 /// <param name="fileInfo"></param>
142 /// <param name="options"></param>
143 /// <param name="password"></param>
144 internal ZipArchive(FileInfo fileInfo, Options options, string password = null)
145 : base(ArchiveType.Zip, fileInfo, options)
147 headerFactory = new SeekableZipHeaderFactory(password);
150 protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file, Options options)
152 if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
154 options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
156 return new ZipVolume(file.OpenRead(), options).AsEnumerable();
160 internal ZipArchive()
161 : base(ArchiveType.Zip)
166 /// Takes multiple seekable Streams for a multi-part archive
168 /// <param name="stream"></param>
169 /// <param name="options"></param>
170 /// <param name="password"></param>
171 internal ZipArchive(Stream stream, Options options, string password = null)
172 : base(ArchiveType.Zip, stream, options)
174 headerFactory = new SeekableZipHeaderFactory(password);
177 protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
179 return new ZipVolume(streams.First(), options).AsEnumerable();
182 protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
184 var volume = volumes.Single();
185 Stream stream = volume.Stream;
186 foreach (ZipHeader h in headerFactory.ReadSeekableHeader(stream))
190 switch (h.ZipHeaderType)
192 case ZipHeaderType.DirectoryEntry:
194 yield return new ZipArchiveEntry(this,
195 new SeekableZipFilePart(headerFactory,
196 h as DirectoryEntryHeader,
200 case ZipHeaderType.DirectoryEnd:
202 byte[] bytes = (h as DirectoryEndHeader).Comment;
203 volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length);
211 protected override void SaveTo(Stream stream, CompressionInfo compressionInfo, Encoding encoding,
212 IEnumerable<ZipArchiveEntry> oldEntries,
213 IEnumerable<ZipArchiveEntry> newEntries)
215 using (var writer = new ZipWriter(stream, compressionInfo, string.Empty, encoding))
217 foreach (var entry in oldEntries.Concat(newEntries)
218 .Where(x => !x.IsDirectory))
220 using (var entryStream = entry.OpenEntryStream())
222 writer.Write(entry.Key, entryStream, entry.LastModifiedTime, string.Empty);
228 protected override ZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
231 return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
234 public static ZipArchive Create()
236 return new ZipArchive();
239 protected override IReader CreateReaderForSolidExtraction()
241 var stream = Volumes.Single().Stream;
243 return ZipReader.Open(stream);