/src/CleanZip.Compression/ZipArchive.cs
https://bitbucket.org/jens13/cleanzip · C# · 237 lines · 205 code · 27 blank · 5 comment · 29 complexity · e3b5f9ffcc7df7bd1bd9c6185bd1f599 MD5 · raw file
- // Copyright Jens Granlund 2012.
- // Distributed under the New BSD License.
- // (See accompanying file notice.txt or at
- // http://www.opensource.org/licenses/bsd-license.php)
- // Source: https://bitbucket.org/jens13/cleanzip
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- namespace CleanZip.Compression
- {
- public abstract class ZipArchive : ZipArchiveBase, IDisposable
- {
- private readonly bool _isReadOnly;
- private readonly List<ZipFile> _mergeArchives = new List<ZipFile>();
- protected ZipArchive(bool isReadOnly)
- {
- _isReadOnly = isReadOnly;
- }
- public Stream BaseStream { get { return ArchiveStream; } }
- public IEnumerable<ZipEntry> Entries { get { return EntryList.ToArray(); } }
- public bool IsReadOnly { get { return _isReadOnly; } }
- public void Dispose()
- {
- Close();
- }
- public abstract void Close();
- public ZipEntry Add(string fileName, CompressionType compressionType)
- {
- string entryName = Path.GetFileName(fileName);
- if (entryName == null)
- throw new ArgumentException(string.Format("{0} is not a file", fileName), "fileName");
- return Add(fileName, entryName, compressionType);
- }
- public ZipEntry Add(string fileName, string entryName, CompressionType compressionType)
- {
- Func<string, ZipEntry> createMethod = name => new ZipEntry(fileName, name, compressionType);
- Action<ZipEntry> setMethod = zipEntry => zipEntry.SetNewFile(fileName, compressionType);
- return ChangeOrCreateZipEntry(entryName, setMethod, createMethod);
- }
- public ZipEntry Add(string fileName, string entryName, CompressionType compressionType, string password)
- {
- Func<string, ZipEntry> createMethod = name => new ZipEntry(fileName, name, compressionType, password);
- Action<ZipEntry> setMethod = zipEntry => zipEntry.SetNewFile(fileName, compressionType, password);
- return ChangeOrCreateZipEntry(entryName, setMethod, createMethod);
- }
- public ZipEntry Add(Stream fileStream, string entryName, CompressionType compressionType)
- {
- Func<string, ZipEntry> createMethod = name => new ZipEntry(fileStream, name, compressionType);
- Action<ZipEntry> setMethod = zipEntry => zipEntry.SetNewStream(fileStream, compressionType);
- return ChangeOrCreateZipEntry(entryName, setMethod, createMethod);
- }
- public ZipEntry Add(Stream fileStream, string entryName, CompressionType compressionType, string password)
- {
- Func<string, ZipEntry> createMethod = name => new ZipEntry(fileStream, name, compressionType, password);
- Action<ZipEntry> setMethod = zipEntry => zipEntry.SetNewStream(fileStream, compressionType, password);
- return ChangeOrCreateZipEntry(entryName, setMethod, createMethod);
- }
- public void Delete(string entryName)
- {
- if (_isReadOnly) throw new ZipException("ZipFile is readonly, delete can not be executed");
- if (IsNew) throw new ZipException("Can not delete from new archive");
- ZipEntry zipEntry = Find(ZipEntry.NormalizeName(entryName));
- if (zipEntry != null)
- {
- EntryList.Remove(zipEntry);
- IsChanged = true;
- IsDirty = true;
- }
- }
- public void ExtractAll(string folder, bool overwrite)
- {
- ExtractAll(folder, overwrite, null);
- }
- public void ExtractAll(string folder, bool overwrite, string password)
- {
- Extract(EntryList, folder, overwrite, password);
- }
- public void Extract(IEnumerable<ZipEntry> entries, string folder, bool overwrite)
- {
- Extract(entries, folder, overwrite, null);
- }
- public void Extract(IEnumerable<ZipEntry> entries, string folder, bool overwrite, string password)
- {
- if (Directory.Exists(folder) && !overwrite)
- throw new IOException(string.Format("Folder {0} already exists", folder));
- foreach (ZipEntry entry in entries)
- {
- string file = Path.Combine(folder, entry.Name.Replace('/', Path.DirectorySeparatorChar));
- Extract(entry, file, overwrite, password);
- }
- }
- public void Extract(ZipEntry entry, string path, bool overwrite)
- {
- Extract(entry, path, overwrite, null);
- }
- public void Extract(ZipEntry entry, string path, bool overwrite, string password)
- {
- if (entry.Name.EndsWith("/"))
- {
- Directory.CreateDirectory(path);
- return;
- }
- if (File.Exists(path) && !overwrite)
- throw new IOException(string.Format("File {0} already exists", path));
- string directoryName = Path.GetDirectoryName(path);
- if (directoryName != null) Directory.CreateDirectory(directoryName);
- using (FileStream stream = File.Create(path))
- {
- entry.Extract(stream, password);
- }
- File.SetCreationTime(path, entry.LastModified);
- File.SetLastWriteTime(path, entry.LastModified);
- }
- protected void FindCentralEndRecord()
- {
- ArchiveStream.Seek(-17, SeekOrigin.End);
- uint signature = 0;
- while (ArchiveStream.Position > 5 && signature != CentralEndRecordSignature)
- {
- ArchiveStream.Seek(-5, SeekOrigin.Current);
- signature = ArchiveStream.ReadUInt();
- }
- ReadCentralEndRecord();
- }
- protected void WriteToStream(Stream stream)
- {
- foreach (var entry in EntryList)
- {
- entry.WriteEntry(stream);
- }
- WriteCentralDirectory(stream);
- WriteCentralEndRecord(stream);
- }
- public void Flush()
- {
- if (_isReadOnly) throw new ZipException("ZipFile is readonly");
- InternalFlush(true);
- }
- protected abstract void InternalFlush(bool leaveOpen);
- protected bool SimpleFlush(bool leaveOpen)
- {
- if (MergeArchivesExists || !IsNew || IsChanged) return false;
- if (IsNew)
- {
- if (AllowReplacingEntries)
- {
- ArchiveStream.Position = 0;
- foreach (var entry in EntryList)
- {
- entry.WriteEntry(ArchiveStream);
- }
- }
- }
- else
- {
- ArchiveStream.Seek(CentralDirectoryOffset, SeekOrigin.Begin);
- foreach (var entry in EntryList.Where(x => x.IsDirty))
- {
- entry.WriteEntry(ArchiveStream);
- }
- }
- WriteCentralDirectory(ArchiveStream);
- WriteCentralEndRecord(ArchiveStream);
- CloseStreamOrLeaveOpen(leaveOpen);
- return true;
- }
- protected void CloseMergeFiles()
- {
- foreach (ZipFile mergeFile in _mergeArchives)
- {
- mergeFile.Close();
- }
- _mergeArchives.Clear();
- }
- protected bool MergeArchivesExists { get { return _mergeArchives.Count > 0; } }
- public void MergeZipFile(string file, bool overwrite)
- {
- if (_isReadOnly) throw new ZipException("ZipFile is readonly, merge can not be executed");
- if (!File.Exists(file)) throw new ZipException(string.Format("File {0} does not exist.", file));
- if (_mergeArchives.FirstOrDefault(x => x.FileName.Equals(file, StringComparison.OrdinalIgnoreCase)) !=
- null) return;
- var zf = ZipFile.OpenRead(file);
- _mergeArchives.Add(zf);
- MergeZipFile(zf, overwrite);
- }
- private void MergeZipFile(ZipFile archive, bool overwrite)
- {
- if (overwrite)
- {
- EntryList.RemoveAll(
- x =>
- archive.Entries.FirstOrDefault(y => y.Name.Equals(x.Name, StringComparison.OrdinalIgnoreCase)) !=
- null);
- EntryList.AddRange(archive.Entries);
- IsDirty = archive.EntryList.Count != 0;
- }
- else
- {
- var zipEntries =
- archive.Entries.Where(
- x =>
- EntryList.FirstOrDefault(y => y.Name.Equals(x.Name, StringComparison.OrdinalIgnoreCase)) == null)
- .ToArray();
- IsDirty = zipEntries.Length != 0;
- EntryList.AddRange(zipEntries);
- }
- }
- }
- }