157 lines
3.9 KiB
C#
157 lines
3.9 KiB
C#
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<string, byte[]> _entries = new ConcurrentDictionary<string, byte[]>(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<string, byte[]> 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<KeyValuePair<string, byte[]>> GetAllEntries()
|
|
{
|
|
return _entries;
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
_entries.Clear();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
_disposed = true;
|
|
_entries.Clear();
|
|
}
|
|
}
|
|
}
|