Added zip and tar.gz archives
This commit is contained in:
71
src/AMWD.Common/Compression/ArchiveFile.cs
Normal file
71
src/AMWD.Common/Compression/ArchiveFile.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
#if NET8_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace AMWD.Common.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// A file to be included in an archive.
|
||||
/// </summary>
|
||||
public class ArchiveFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the file name (including relative path) within the archive.
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binary content.
|
||||
/// </summary>
|
||||
public byte[] Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Unix file permissions (only applicable for tar).
|
||||
/// </summary>
|
||||
public UnixFileMode Permissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Unix file permissions from an octal string (e.g. "0755").
|
||||
/// </summary>
|
||||
/// <param name="oct">The octal file permission.</param>
|
||||
public void SetPermissions(string oct)
|
||||
{
|
||||
int permissions = Convert.ToInt32(oct, 8);
|
||||
SetPermissions(permissions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Unix file permissions from a decimal integer (e.g. 493 for octal 0755).
|
||||
/// </summary>
|
||||
/// <param name="dec">The decimal file permission.</param>
|
||||
public void SetPermissions(int dec)
|
||||
{
|
||||
// Keep only permission and special bits (octal 07777)
|
||||
int bits = dec & 0xFFF;
|
||||
|
||||
UnixFileMode result = 0;
|
||||
|
||||
if ((bits & 0b100000000) != 0) result |= UnixFileMode.UserRead; // 0400
|
||||
if ((bits & 0b010000000) != 0) result |= UnixFileMode.UserWrite; // 0200
|
||||
if ((bits & 0b001000000) != 0) result |= UnixFileMode.UserExecute; // 0100
|
||||
|
||||
if ((bits & 0b000100000) != 0) result |= UnixFileMode.GroupRead; // 0040
|
||||
if ((bits & 0b000010000) != 0) result |= UnixFileMode.GroupWrite; // 0020
|
||||
if ((bits & 0b000001000) != 0) result |= UnixFileMode.GroupExecute; // 0010
|
||||
|
||||
if ((bits & 0b000000100) != 0) result |= UnixFileMode.OtherRead; // 0004
|
||||
if ((bits & 0b000000010) != 0) result |= UnixFileMode.OtherWrite; // 0002
|
||||
if ((bits & 0b000000001) != 0) result |= UnixFileMode.OtherExecute; // 0001
|
||||
|
||||
// Special bits
|
||||
if ((bits & 0b100000000000) != 0) result |= UnixFileMode.SetUser; // 4000
|
||||
if ((bits & 0b010000000000) != 0) result |= UnixFileMode.SetGroup; // 2000
|
||||
if ((bits & 0b001000000000) != 0) result |= UnixFileMode.StickyBit; // 1000
|
||||
|
||||
Permissions = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
91
src/AMWD.Common/Compression/ArchiveHelper.cs
Normal file
91
src/AMWD.Common/Compression/ArchiveHelper.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
#if NET8_0_OR_GREATER
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Formats.Tar;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Common.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helper methods for creating zip and tar.gz archives from collections of files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All methods in this class are static and thread-safe. The returned archive data is provided as a
|
||||
/// byte array in memory, suitable for saving to disk or transmitting over a network. This class does not perform any
|
||||
/// validation on file contents or names; callers are responsible for ensuring that input files are valid and
|
||||
/// appropriate for archiving.
|
||||
/// </remarks>
|
||||
public static class ArchiveHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a ZIP archive containing the specified files and returns its contents as a byte array.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The resulting ZIP archive will contain one entry for each file in the collection, using the
|
||||
/// file's specified name and content. The method uses optimal compression for all entries.
|
||||
/// </remarks>
|
||||
/// <param name="files">A read-only collection of files to include in the ZIP archive. Each file must specify a file name and its content.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation. The default value is none.</param>
|
||||
/// <returns>A byte array containing the contents of the created ZIP archive. The array will be empty if no files are provided.</returns>
|
||||
public static async Task<byte[]> CreateZip(IReadOnlyCollection<ArchiveFile> files, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: false))
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
var entry = zip.CreateEntry(file.FileName, CompressionLevel.Optimal);
|
||||
using var entryStream = entry.Open();
|
||||
await entryStream.WriteAsync(file.Content, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
await ms.FlushAsync(cancellationToken);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a GZip-compressed tar archive containing the specified files, using the given tar entry format.
|
||||
/// </summary>
|
||||
/// <remarks>The returned archive is written to memory and may consume significant resources for large file
|
||||
/// collections. The order of files in the archive matches the order in the input collection.</remarks>
|
||||
/// <param name="files">The collection of files to include in the archive. Each file must specify a file name and its content. Cannot be
|
||||
/// null or contain null elements.</param>
|
||||
/// <param name="format">The tar entry format to use for the archive. Defaults to Pax if not specified.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A byte array containing the GZip-compressed tar archive with the specified files.</returns>
|
||||
public static async Task<byte[]> CreateTarGz(IReadOnlyCollection<ArchiveFile> files, TarEntryFormat format = TarEntryFormat.Pax, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
using (var gzip = new GZipStream(ms, CompressionLevel.Optimal, leaveOpen: false))
|
||||
using (var tar = new TarWriter(gzip, format, leaveOpen: false))
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
PosixTarEntry entry = format switch
|
||||
{
|
||||
TarEntryFormat.Gnu => new GnuTarEntry(TarEntryType.RegularFile, file.FileName),
|
||||
TarEntryFormat.Ustar => new UstarTarEntry(TarEntryType.RegularFile, file.FileName),
|
||||
_ => new PaxTarEntry(TarEntryType.RegularFile, file.FileName)
|
||||
};
|
||||
|
||||
entry.Mode = file.Permissions;
|
||||
|
||||
using var stream = new MemoryStream(file.Content);
|
||||
entry.DataStream = stream;
|
||||
await tar.WriteEntryAsync(entry, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
await ms.FlushAsync(cancellationToken);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user