using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Text; namespace Intelix.Helper.Data; public sealed class InMemoryZip : IDisposable { private readonly ConcurrentDictionary _entries = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly object _buildLock = new object(); private bool _disposed; private string _rootFolderPrefix = string.Empty; public int Count => _entries.Count; public void SetRootFolderPrefix(string prefix) { if (string.IsNullOrEmpty(prefix)) { _rootFolderPrefix = string.Empty; } else { _rootFolderPrefix = NormalizeEntryName(prefix) + "/"; } } private static string NormalizeEntryName(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Entry name is null or empty", "name"); } name = name.Replace('\\', '/').Trim('/'); if (name.Length != 0) { return name; } throw new ArgumentException("Invalid entry name", "name"); } private string ApplyRootPrefix(string entryPath) { if (string.IsNullOrEmpty(_rootFolderPrefix)) { return entryPath; } string normalized = NormalizeEntryName(entryPath); if (normalized.StartsWith(_rootFolderPrefix, StringComparison.OrdinalIgnoreCase)) { return normalized; } return _rootFolderPrefix + normalized; } public void AddFile(string entryPath, byte[] content) { if (_disposed) { throw new ObjectDisposedException("InMemoryZip"); } if (content != null && content.Length != 0) { string key = ApplyRootPrefix(entryPath); byte[] copy = new byte[content.Length]; Buffer.BlockCopy(content, 0, copy, 0, content.Length); _entries.AddOrUpdate(key, copy, (string text, byte[] old) => copy); } } public void AddTextFile(string entryPath, string text) { if (!string.IsNullOrEmpty(text)) { AddFile(entryPath, Encoding.UTF8.GetBytes(text)); } } public void AddDirectoryFiles(string sourceDirectory, string targetEntryDirectory = "", bool recursive = true) { if (_disposed) { throw new ObjectDisposedException("InMemoryZip"); } if (string.IsNullOrEmpty(sourceDirectory)) { throw new ArgumentException("sourceDirectory"); } if (!Directory.Exists(sourceDirectory)) { return; } SearchOption searchOption = (recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); string[] files = Directory.GetFiles(sourceDirectory, "*", searchOption); foreach (string text in files) { string text2 = text.Substring(sourceDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); string text3 = (string.IsNullOrEmpty(targetEntryDirectory) ? text2 : Path.Combine(targetEntryDirectory, text2)); text3 = text3.Replace('\\', '/'); try { byte[] content = File.ReadAllBytes(text); AddFile(text3, content); } catch { } } } public byte[] ToArray(CompressionLevel compression = CompressionLevel.Fastest) { if (_disposed) { throw new ObjectDisposedException("InMemoryZip"); } lock (_buildLock) { using MemoryStream memoryStream = new MemoryStream(); using (ZipArchive zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true, Encoding.UTF8)) { foreach (KeyValuePair entry in _entries) { using Stream stream = zipArchive.CreateEntry(entry.Key, compression).Open(); byte[] value = entry.Value; stream.Write(value, 0, value.Length); } } return memoryStream.ToArray(); } } public IEnumerable> GetAllEntries() { return _entries; } public void Clear() { _entries.Clear(); } public void Dispose() { if (!_disposed) { _disposed = true; _entries.Clear(); } } }