124 lines
4.3 KiB
C#
124 lines
4.3 KiB
C#
using System.Security.Cryptography;
|
|
|
|
namespace RedundancyFinder
|
|
{
|
|
public class Finder
|
|
{
|
|
List<Task> tasks = new List<Task>();
|
|
|
|
Dictionary<string, Redundancy> redundancies = new Dictionary<string, Redundancy>();
|
|
|
|
public Dictionary<string, Redundancy> Redundancies { get => redundancies; }
|
|
|
|
string[] extensions;
|
|
|
|
public event EventHandler<DirectoryErrorEventArgs>? DirectoryError;
|
|
public event EventHandler<FileErrorEventArgs>? FileError;
|
|
public event EventHandler<FileFoundEventArgs>? FileFound;
|
|
public event EventHandler<string>? TaskStarted;
|
|
public event EventHandler<ProcessingFileEventArgs>? ProcessingFile;
|
|
public void FindRedundancies(string[] paths, string[] extensions)
|
|
{
|
|
this.extensions = extensions;
|
|
foreach (var path in paths)
|
|
{
|
|
if (Directory.Exists(path))
|
|
{
|
|
ProcessDirectory(path);
|
|
}
|
|
else if (File.Exists(path))
|
|
{
|
|
ProcessFile(path);
|
|
}
|
|
}
|
|
|
|
// Wait for all tasks to complete
|
|
Task.WaitAll(tasks.ToArray());
|
|
}
|
|
|
|
private void ProcessDirectory(string directoryPath)
|
|
{
|
|
try
|
|
{
|
|
// Process files in the current directory
|
|
foreach (var file in Directory.GetFiles(directoryPath))
|
|
{
|
|
ProcessFile(file);
|
|
}
|
|
|
|
// Recursively process subdirectories
|
|
foreach (var subDirectory in Directory.GetDirectories(directoryPath))
|
|
{
|
|
try
|
|
{
|
|
ProcessDirectory(subDirectory);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DirectoryError?.Invoke(this, new DirectoryErrorEventArgs() { Exception = ex, Path = subDirectory });
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DirectoryError?.Invoke(this, new DirectoryErrorEventArgs() { Exception = ex, Path = directoryPath });
|
|
}
|
|
}
|
|
|
|
|
|
private void ProcessFile(string filePath)
|
|
{
|
|
if (!extensions.Contains(Path.GetExtension(filePath)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Task task = new(() =>
|
|
{
|
|
ProcessingFile?.Invoke(this, new ProcessingFileEventArgs() { Path = filePath });
|
|
|
|
try
|
|
{
|
|
|
|
var fileHash = ComputeFileHash(filePath);
|
|
if (fileHash == null)
|
|
{
|
|
return;
|
|
}
|
|
long fileSize = new FileInfo(filePath).Length;
|
|
lock (redundancies)
|
|
{
|
|
if (redundancies.ContainsKey(fileHash))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!redundancies.ContainsKey(fileHash))
|
|
{
|
|
var redundancy = new Redundancy() { Hash = fileHash, FileSize = fileSize };
|
|
redundancies.Add(fileHash, redundancy);
|
|
}
|
|
redundancies[fileHash].Paths.Add(filePath);
|
|
}
|
|
FileFound?.Invoke(this, new FileFoundEventArgs(filePath, fileHash, fileSize));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
FileError?.Invoke(this, new FileErrorEventArgs() { Exception = ex, Path = filePath });
|
|
}
|
|
});
|
|
task.Start();
|
|
TaskStarted?.Invoke(this, filePath);
|
|
tasks.Add(task);
|
|
}
|
|
|
|
private static string ComputeFileHash(string filePath)
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
using var stream = File.OpenRead(filePath);
|
|
var hash = sha256.ComputeHash(stream);
|
|
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
|
}
|
|
}
|
|
}
|