2 using System.Collections.Generic;
5 using SharpCompress.Common;
6 using SharpCompress.Common.Zip;
7 using SharpCompress.Common.Zip.Headers;
8 using SharpCompress.Compressor.Deflate;
9 using SharpCompress.Reader;
10 using SharpCompress.Reader.Zip;
11 using SharpCompress.Writer.Zip;
13 namespace SharpCompress.Archive.Zip
15 internal class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
17 private readonly SeekableZipHeaderFactory headerFactory;
20 /// Gets or sets the compression level applied to files added to the archive,
21 /// if the compression method is set to deflate
23 public CompressionLevel DeflateCompressionLevel { get; set; }
25 #if !PORTABLE && !NETFX_CORE
27 /// Constructor expects a filepath to an existing file.
29 /// <param name="filePath"></param>
30 /// <param name="password"></param>
31 public static ZipArchive Open(string filePath, string password = null)
33 return Open(filePath, Options.None, password);
37 /// Constructor with a FileInfo object to an existing file.
39 /// <param name="fileInfo"></param>
40 /// <param name="password"></param>
41 public static ZipArchive Open(FileInfo fileInfo, string password = null)
43 return Open(fileInfo, Options.None, password);
47 /// Constructor expects a filepath to an existing file.
49 /// <param name="filePath"></param>
50 /// <param name="options"></param>
51 /// <param name="password"></param>
52 public static ZipArchive Open(string filePath, Options options, string password = null)
54 filePath.CheckNotNullOrEmpty("filePath");
55 return Open(new FileInfo(filePath), options, password);
59 /// Constructor with a FileInfo object to an existing file.
61 /// <param name="fileInfo"></param>
62 /// <param name="options"></param>
63 /// <param name="password"></param>
64 public static ZipArchive Open(FileInfo fileInfo, Options options, string password = null)
66 fileInfo.CheckNotNull("fileInfo");
67 return new ZipArchive(fileInfo, options, password);
72 /// Takes a seekable Stream as a source
74 /// <param name="stream"></param>
75 /// <param name="password"></param>
76 public static ZipArchive Open(Stream stream, string password = null)
78 stream.CheckNotNull("stream");
79 return Open(stream, Options.None, password);
83 /// Takes a seekable Stream as a source
85 /// <param name="stream"></param>
86 /// <param name="options"></param>
87 /// <param name="password"></param>
88 public static ZipArchive Open(Stream stream, Options options, string password = null)
90 stream.CheckNotNull("stream");
91 return new ZipArchive(stream, options, password);
94 #if !PORTABLE && !NETFX_CORE
95 public static bool IsZipFile(string filePath, string password = null)
97 return IsZipFile(new FileInfo(filePath), password);
100 public static bool IsZipFile(FileInfo fileInfo, string password = null)
102 if (!fileInfo.Exists)
106 using (Stream stream = fileInfo.OpenRead())
108 return IsZipFile(stream, password);
113 public static bool IsZipFile(Stream stream, string password = null)
115 StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory(password);
119 headerFactory.ReadStreamHeader(stream).FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
124 return Enum.IsDefined(typeof (ZipHeaderType), header.ZipHeaderType);
126 catch (CryptographicException)
136 #if !PORTABLE && !NETFX_CORE
138 /// Constructor with a FileInfo object to an existing file.
140 /// <param name="fileInfo"></param>
141 /// <param name="options"></param>
142 /// <param name="password"></param>
143 internal ZipArchive(FileInfo fileInfo, Options options, string password = null)
144 : base(ArchiveType.Zip, fileInfo, options)
146 headerFactory = new SeekableZipHeaderFactory(password);
149 protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file, Options options)
151 if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
153 options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
155 return new ZipVolume(file.OpenRead(), options).AsEnumerable();
159 internal ZipArchive()
160 : base(ArchiveType.Zip)
165 /// Takes multiple seekable Streams for a multi-part archive
167 /// <param name="stream"></param>
168 /// <param name="options"></param>
169 /// <param name="password"></param>
170 internal ZipArchive(Stream stream, Options options, string password = null)
171 : base(ArchiveType.Zip, stream, options)
173 headerFactory = new SeekableZipHeaderFactory(password);
176 protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
178 return new ZipVolume(streams.First(), options).AsEnumerable();
181 protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
183 var volume = volumes.Single();
184 Stream stream = volume.Stream;
185 foreach (ZipHeader h in headerFactory.ReadSeekableHeader(stream))
189 switch (h.ZipHeaderType)
191 case ZipHeaderType.DirectoryEntry:
193 yield return new ZipArchiveEntry(this,
194 new SeekableZipFilePart(headerFactory,
195 h as DirectoryEntryHeader,
199 case ZipHeaderType.DirectoryEnd:
201 byte[] bytes = (h as DirectoryEndHeader).Comment;
202 volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length);
210 protected override void SaveTo(Stream stream, CompressionInfo compressionInfo,
211 IEnumerable<ZipArchiveEntry> oldEntries,
212 IEnumerable<ZipArchiveEntry> newEntries)
214 using (var writer = new ZipWriter(stream, compressionInfo, string.Empty))
216 foreach (var entry in oldEntries.Concat(newEntries)
217 .Where(x => !x.IsDirectory))
219 using (var entryStream = entry.OpenEntryStream())
221 writer.Write(entry.Key, entryStream, entry.LastModifiedTime, string.Empty);
227 protected override ZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
230 return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
233 public static ZipArchive Create()
235 return new ZipArchive();
238 protected override IReader CreateReaderForSolidExtraction()
240 var stream = Volumes.Single().Stream;
242 return ZipReader.Open(stream);