Anul 3 Semestrul 2

This commit is contained in:
2025-07-03 20:56:38 +03:00
parent 184f3bd92e
commit 3b7fb85767
269 changed files with 20955 additions and 0 deletions
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,57 @@
using System;
using FileCompression.Enums;
using FileCompression.Services;
using FileCompression.Services.Strategies;
namespace FileCompression;
public class Compressor
{
private static Compressor? _compressor;
private static readonly Lock _lock = new();
private static readonly StrategyContext StrategyContext = new();
private Compressor()
{
}
public static Compressor Instance
{
get
{
lock (_lock)
{
if (_compressor == null)
{
_compressor = new Compressor();
}
return _compressor;
}
}
}
public void Compress(string sourcePath, string destinationPath, CompressionMode mode)
{
if (string.IsNullOrEmpty(sourcePath))
{
throw new ArgumentNullException(nameof(sourcePath), "Source path cannot be null or empty.");
}
if (string.IsNullOrEmpty(destinationPath))
{
throw new ArgumentNullException(nameof(destinationPath), "Destination path cannot be null or empty.");
}
if (File.Exists(destinationPath))
{
File.Delete(destinationPath);
}
StrategyContext.SetStrategy(CompressorFactory.CreateCompressorStrategy(mode));
var compressor = CompressorFactory.CreateCompressor(sourcePath, destinationPath, mode, StrategyContext);
compressor.Compress();
Console.WriteLine($"Compressed {sourcePath} to {destinationPath} using {mode} mode.");
Console.WriteLine($"Compression completed successfully.");
}
}
@@ -0,0 +1,8 @@
namespace FileCompression.Enums;
public enum CompressionMode
{
Zip,
Tar,
TarGz
}
@@ -0,0 +1,7 @@
namespace FileCompression.Enums;
public enum EntryType
{
File,
Directory
}
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,9 @@
using System;
namespace FileCompression.Interfaces;
public interface ICompressionStrategy
{
void CompressFile(string sourcePath, string destinationPath, string basePath);
void CompressDirectory(string sourcePath, string destinationPath, string basePath);
}
@@ -0,0 +1,11 @@
using System;
using System.IO.Compression;
using FileCompression.Services.Strategies;
namespace FileCompression.Interfaces;
public interface ICompressor
{
void Compress();
}
@@ -0,0 +1,10 @@
using System;
using FileCompression.Enums;
namespace FileCompression.Models;
public class Entry
{
public required string Path { get; set; }
public EntryType Type { get; set; }
}
@@ -0,0 +1,25 @@
using System;
using FileCompression.Interfaces;
using FileCompression.Services.Strategies;
namespace FileCompression.Services.Composites;
public class FileCompressor : ICompressor
{
private string sourcePath;
private string destinationPath;
private string basePath;
private StrategyContext strategyContext;
public FileCompressor(string sourcePath, string destinationPath, string basePath, StrategyContext strategyContext)
{
this.sourcePath = sourcePath;
this.destinationPath = destinationPath;
this.strategyContext = strategyContext;
this.basePath = basePath;
}
public void Compress()
{
strategyContext.CompressFile(sourcePath, destinationPath, basePath);
}
}
@@ -0,0 +1,48 @@
using System;
using FileCompression.Enums;
using FileCompression.Interfaces;
using FileCompression.Models;
using FileCompression.Services.Strategies;
namespace FileCompression.Services.Composites;
public class FolderCompressor : ICompressor
{
private readonly string sourcePath;
private readonly string destinationPath;
private readonly StrategyContext strategyContext;
private readonly string basePath;
public FolderCompressor(string sourcePath, string destinationPath, string basePath, StrategyContext strategyContext)
{
this.sourcePath = sourcePath;
this.destinationPath = destinationPath;
this.strategyContext = strategyContext;
this.basePath = basePath;
}
public void Compress()
{
strategyContext.CompressDirectory(sourcePath, destinationPath, basePath);
var entries = GetEntries();
foreach (var entry in entries)
{
entry.Compress();
}
}
private List<ICompressor> GetEntries()
{
List<ICompressor> entries = [];
foreach (var file in Directory.GetFiles(sourcePath))
{
entries.Add(new FileCompressor(file, destinationPath, basePath, strategyContext));
}
foreach (var directory in Directory.GetDirectories(sourcePath))
{
entries.Add(new FolderCompressor(directory, destinationPath, basePath, strategyContext));
}
return entries;
}
}
@@ -0,0 +1,43 @@
using System;
using System.IO.Compression;
using FileCompression.Interfaces;
using FileCompression.Services.Strategies;
namespace FileCompression.Services.Composites;
public class TarCompressor : ICompressor
{
private readonly string sourcePath;
private readonly string destinationPath;
private readonly StrategyContext strategyContext;
private readonly string basePath;
private readonly string tarPath;
private ICompressor compressor;
public TarCompressor(string sourcePath, string destinationPath, string basePath, StrategyContext strategyContext)
{
this.sourcePath = sourcePath;
this.destinationPath = destinationPath;
this.strategyContext = strategyContext;
this.basePath = basePath;
this.tarPath = destinationPath + ".tar";
compressor = File.Exists(sourcePath) ? new FileCompressor(sourcePath, tarPath, basePath, strategyContext) : new FolderCompressor(sourcePath, tarPath, basePath, strategyContext);
}
public void Compress()
{
compressor.Compress();
GZipCompress();
}
private void GZipCompress()
{
using (var fileStream = new FileStream(tarPath, FileMode.Open))
using (var compressedFileStream = File.Create(destinationPath))
using (var compressionStream = new GZipStream(compressedFileStream, CompressionLevel.Optimal))
{
fileStream.CopyTo(compressionStream);
}
File.Delete(tarPath);
}
}
@@ -0,0 +1,59 @@
using System;
using FileCompression.Enums;
using FileCompression.Interfaces;
using FileCompression.Services.Composites;
using FileCompression.Services.Strategies;
namespace FileCompression.Services;
public static class CompressorFactory
{
public static ICompressionStrategy CreateCompressorStrategy(CompressionMode mode)
{
return mode switch
{
CompressionMode.Zip => new ZipStrategy(),
CompressionMode.Tar => new TarGzStrategy(),
CompressionMode.TarGz => new TarGzStrategy(),
_ => throw new NotSupportedException($"Compression mode '{mode}' is not supported.")
};
}
public static ICompressor CreateCompressor(string sourcePath, string destinationPath, CompressionMode mode, StrategyContext strategyContext)
{
string basePath = sourcePath.Substring(0, sourcePath.LastIndexOf(Path.DirectorySeparatorChar));
basePath = basePath.Substring(0, sourcePath.LastIndexOf(Path.DirectorySeparatorChar));
Console.WriteLine($"Base path: {basePath}");
if (File.Exists(sourcePath))
{
switch (mode)
{
case CompressionMode.Zip:
return new FileCompressor(sourcePath, destinationPath, basePath, strategyContext);
case CompressionMode.Tar:
return new FileCompressor(sourcePath, destinationPath, basePath, strategyContext);
case CompressionMode.TarGz:
return new TarCompressor(sourcePath, destinationPath, basePath, strategyContext);
default:
throw new NotSupportedException($"Compression mode '{mode}' is not supported.");
}
}
if (Directory.Exists(sourcePath))
{
switch (mode)
{
case CompressionMode.Zip:
return new FolderCompressor(sourcePath, destinationPath, basePath, strategyContext);
case CompressionMode.Tar:
return new FolderCompressor(sourcePath, destinationPath, basePath, strategyContext);
case CompressionMode.TarGz:
return new TarCompressor(sourcePath, destinationPath, basePath, strategyContext);
default:
throw new NotSupportedException($"Compression mode '{mode}' is not supported.");
}
}
throw new ArgumentException("Source path must be a file or directory.", nameof(sourcePath));
}
}
@@ -0,0 +1,36 @@
using System;
using FileCompression.Interfaces;
namespace FileCompression.Services.Strategies;
public class StrategyContext
{
private ICompressionStrategy? _compressionStrategy;
public StrategyContext()
{
}
public StrategyContext(ICompressionStrategy compressionStrategy)
{
_compressionStrategy = compressionStrategy;
}
public void SetStrategy(ICompressionStrategy compressionStrategy)
{
_compressionStrategy = compressionStrategy;
}
public void CompressFile(string sourcePath, string destinationPath, string basePath)
{
if (_compressionStrategy == null)
{
throw new InvalidOperationException("Compression strategy is not set.");
}
_compressionStrategy.CompressFile(sourcePath, destinationPath, basePath);
}
public void CompressDirectory(string sourcePath, string destinationPath, string basePath)
{
if (_compressionStrategy == null)
{
throw new InvalidOperationException("Compression strategy is not set.");
}
_compressionStrategy.CompressDirectory(sourcePath, destinationPath, basePath);
}
}
@@ -0,0 +1,68 @@
using System;
using System.Formats.Tar;
using FileCompression.Interfaces;
using FileCompression.Utils;
namespace FileCompression.Services.Strategies;
public class TarGzStrategy : ICompressionStrategy
{
public void CompressFile(string sourcePath, string destinationPath, string basePath)
{
Console.WriteLine($"Compressing {sourcePath} to {destinationPath} using tar and gzip.");
string tempPath = Path.GetTempFileName();
using (var tempStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write))
using (var tempWriter = new TarWriter(tempStream, TarEntryFormat.Pax))
{
// Copy existing entries to temp file
if (File.Exists(destinationPath))
{
using (var originalStream = new FileStream(destinationPath, FileMode.Open, FileAccess.Read))
using (var tarReader = new TarReader(originalStream, leaveOpen: false))
{
TarEntry? entry;
while ((entry = tarReader.GetNextEntry()) is not null)
{
tempWriter.WriteEntry(entry);
}
}
}
// Add new entry
string entryName = ArchiveUtils.EntryFromPath(sourcePath.AsSpan(basePath.Length));
tempWriter.WriteEntry(sourcePath, entryName);
}
File.Copy(tempPath, destinationPath, overwrite: true);
File.Delete(tempPath);
}
public void CompressDirectory(string sourcePath, string destinationPath, string basePath)
{
Console.WriteLine($"Compressing {sourcePath} to {destinationPath} using tar and gzip.");
string tempPath = Path.GetTempFileName();
using (var tempStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write))
using (var tempWriter = new TarWriter(tempStream, TarEntryFormat.Pax))
{
// Copy existing entries to temp file
if (File.Exists(destinationPath))
{
using (var originalStream = new FileStream(destinationPath, FileMode.Open, FileAccess.Read))
using (var tarReader = new TarReader(originalStream, leaveOpen: false))
{
TarEntry? entry;
while ((entry = tarReader.GetNextEntry()) is not null)
{
tempWriter.WriteEntry(entry);
}
}
}
// Add new entry
string entryName = ArchiveUtils.EntryFromPath(sourcePath.AsSpan(basePath.Length), appendPathSeparator: true);
tempWriter.WriteEntry(sourcePath, entryName);
}
}
}
@@ -0,0 +1,72 @@
using System;
using FileCompression.Interfaces;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using FileCompression.Utils;
namespace FileCompression.Services.Strategies;
public class ZipStrategy : ICompressionStrategy
{
public void CompressFile(string sourcePath, string destinationPath, string basePath)
{
Console.WriteLine($"Compressing file {sourcePath} to {destinationPath}");
using (Stream destination = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using ZipArchive archive = new ZipArchive(destination, ZipArchiveMode.Update);
string entryName = ArchiveUtils.EntryFromPath(sourcePath.AsSpan(basePath.Length));
DoCreateEntryFromFile(archive, sourcePath, entryName, CompressionLevel.Optimal);
}
}
public void CompressDirectory(string sourcePath, string destinationPath, string basePath)
{
Console.WriteLine($"Compressing directory {sourcePath} to {destinationPath}");
using (Stream destination = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using ZipArchive archive = new ZipArchive(destination, ZipArchiveMode.Update);
string entryName = ArchiveUtils.EntryFromPath(sourcePath.AsSpan(basePath.Length), appendPathSeparator: true);
archive.CreateEntry(entryName);
}
}
private ZipArchiveEntry DoCreateEntryFromFile(ZipArchive destination,
string sourceFileName, string entryName, CompressionLevel? compressionLevel)
{
ArgumentNullException.ThrowIfNull(destination);
ArgumentNullException.ThrowIfNull(sourceFileName);
ArgumentNullException.ThrowIfNull(entryName);
// Checking of compressionLevel is passed down to DeflateStream and the IDeflater implementation
// as it is a pluggable component that completely encapsulates the meaning of compressionLevel.
// Argument checking gets passed down to FileStream's ctor and CreateEntry
using (FileStream fs = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false))
{
ZipArchiveEntry entry = compressionLevel.HasValue
? destination.CreateEntry(entryName, compressionLevel.Value)
: destination.CreateEntry(entryName);
DateTime lastWrite = File.GetLastWriteTime(sourceFileName);
// If file to be archived has an invalid last modified time, use the first datetime representable in the Zip timestamp format
// (midnight on January 1, 1980):
if (lastWrite.Year < 1980 || lastWrite.Year > 2107)
lastWrite = new DateTime(1980, 1, 1, 0, 0, 0);
entry.LastWriteTime = lastWrite;
using (Stream es = entry.Open())
fs.CopyTo(es);
return entry;
}
}
}
@@ -0,0 +1,26 @@
using System;
namespace FileCompression.Utils;
public static class ArchiveUtils
{
public static string EntryFromPath(ReadOnlySpan<char> path, bool appendPathSeparator = false)
{
// Remove leading separators.
int nonSlash = path.IndexOfAnyExcept('/');
if (nonSlash < 0)
{
nonSlash = path.Length;
}
path = path.Slice(nonSlash);
// Append a separator if necessary.
return (path.IsEmpty, appendPathSeparator) switch
{
(false, false) => path.ToString(),
(false, true) => string.Concat(path, "/"),
(true, false) => string.Empty,
(true, true) => "/",
};
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../FileCompression/FileCompression.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
// See https://aka.ms/new-console-template for more information
using System;
using FileCompression.Interfaces;
using FileCompression.Services.Strategies;
using FileCompression.Services.Composites;
using FileCompression;
using FileCompression.Enums;
// dotnet run --project FileCompressionCLI/ /home/danielcujba/Pictures/Screenshots/ /home/danielcujba/Downloads/destination.zip Zip
// dotnet run --project FileCompressionCLI/ /home/danielcujba/Pictures/Screenshots/ /home/danielcujba/Downloads/destination.tar Tar
// dotnet run --project FileCompressionCLI/ /home/danielcujba/Pictures/Screenshots/ /home/danielcujba/Downloads/destination.tar.gz TarGz
namespace FileCompressionCLI
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 3)
{
Console.WriteLine("Usage: FileCompressionCLI <sourcePath> <destinationPath> <compressionMode>");
return;
}
string sourcePath = args[0];
string destinationPath = args[1];
if (!Enum.TryParse(args[2], true, out CompressionMode mode))
{
Console.WriteLine($"Invalid compression mode: {args[2]}");
return;
}
Compressor.Instance.Compress(sourcePath, destinationPath, mode);
}
}
}