I am writing an application that either mirrors a file/directory into another location or creates a zip file "snap shot" of a file or directory and stores it in another location. The user is able to create "rules" where they define the source file/directory, the destination file/directory and if they are archiving how many archives to keep. The user is also able to exclude files/directories if they so choose. Now this service class is a lot of code and I am wondering if there is a better way to do this. Of course all of the methods that can be called by the interface are called with a BackgroundWorker.RunAsync() method. Any feed back on how to make this more efficient would be super appreciated.
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Packaging; using System.Linq; using System.Net.Mime; using System.Windows.Forms; using Backupr.Helpers; using Backupr.Models; namespace Backupr.Services { public interface IFileActionService { void ArchiveRule(BackupRule rule); void EndOfDay(); } public class FileActionService : IFileActionService { public void ArchiveRule(BackupRule rule) { ArchiveAction archiver = new ArchiveAction(rule); archiver.MoveFilesToTemp(); if (rule.Type == RuleType.Archive) { archiver.ZipTempFile(); archiver.MoveArchiveToDestinations(); } else if (rule.Type == RuleType.Mirror) { archiver.MirrorRule(); } archiver.CleanTempDirectory(); archiver.CleanOldBackups(); } public void EndOfDay() { IRuleService ruleService = new RuleService(); MessageBox.Show("Warning this action will reboot your PC!"); foreach (BackupRule rule in ruleService.GetAll().Where(r => r.EndOfDay == true)) { ArchiveRule(rule); } Process.Start("shutdown.exe", "/r /f /t 1"); } } // HACK: need to find a better way to handle deleted files and directories. // perhaps a logging system that lets the user know the file is missing // or just removing the deleted file from the rule. // refer to MoveFilesToTemp(), MirrorRule(), DirectoryCopy() internal class ArchiveAction { // TODO: allow users to define archiveName via built in tokens. private string archiveName = System.Environment.MachineName + '_' + DateTime.Now.ToString("yyyyMMddhhmmss"); private BackupRule rule; private List<string> excludes = new List<string>(); public ArchiveAction(BackupRule _rule) { rule = _rule; foreach (BackupPath excludePath in rule.Paths.Where(p => p.Type == PathType.Exclude)) { excludes.Add(excludePath.Value); } } public void MoveFilesToTemp() { foreach (BackupPath sourcePath in rule.Paths.Where(p => p.Type == PathType.Source)) { // See comment at top of ArchiveAction Class if (PathIsDirectory(sourcePath.Value)) { DirectoryCopy(sourcePath.Value, Path.Combine(tempDirectory, Path.GetFileName(sourcePath.Value)), true); } else if (File.Exists(sourcePath.Value) && !excludes.Any(p => p == sourcePath.Value)) { File.Copy(sourcePath.Value, Path.Combine(tempDirectory, Path.GetFileName(sourcePath.Value))); } } } public void ZipTempFile() { using (Package zip = Package.Open(Path.Combine(Asset.BackuprTempDirectory, archiveName + ".zip"), FileMode.Create)) { foreach (string file in Directory.GetFiles(tempDirectory, "*.*", SearchOption.AllDirectories)) { string packageFile = file.Replace(' ', '_'); Uri uri = new Uri(packageFile.Substring(tempDirectory.Length), UriKind.Relative); PackagePart ZipPart = zip.CreatePart(PackUriHelper.CreatePartUri(uri), MediaTypeNames.Application.Zip, CompressionOption.Maximum); byte[] B = File.ReadAllBytes(file); ZipPart.GetStream().Write(B, 0, B.Length); } } } public void MoveArchiveToDestinations() { foreach (BackupPath destinationPath in rule.Paths.Where(p => p.Type == PathType.Destination)) { if (!Directory.Exists(destinationPath.Value)) { Directory.CreateDirectory(destinationPath.Value); } File.Copy(Path.Combine(Asset.BackuprTempDirectory, archiveName + ".zip"), Path.Combine(destinationPath.Value, archiveName + ".zip")); } } public void MirrorRule() { foreach (BackupPath destinationPath in rule.Paths.Where(p => p.Type == PathType.Destination)) { DirectoryCopy(tempDirectory, destinationPath.Value, true); } } public void CleanTempDirectory() { Directory.Delete(Asset.BackuprTempDirectory, true); } public void CleanOldBackups() { if (rule.Type == RuleType.Archive) { foreach (BackupPath destinationPath in rule.Paths.Where(p => p.Type == PathType.Destination)) { DirectoryInfo directory = new DirectoryInfo(destinationPath.Value); if (!directory.Exists) { // See comment at top of ArchiveAction Class break; } FileInfo[] files = directory.GetFiles(); foreach (FileInfo file in files.OrderByDescending(p => p.LastWriteTime).Skip(rule.BackupsToKeep)) { string filePath = Path.Combine(destinationPath.Value, file.Name); File.Delete(filePath); } } } } private bool PathIsDirectory(string path) { FileAttributes fileAttribute = File.GetAttributes(@path); if ((fileAttribute & FileAttributes.Directory) == FileAttributes.Directory) { return true; } return false; } private string tempDirectory { get { string directory = Path.Combine(Asset.BackuprTempDirectory, archiveName); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } return directory; } } private void DirectoryCopy(string source, string destination, bool copySubDirectories) { if (excludes.Any(p => p == source)) { return; } DirectoryInfo directory = new DirectoryInfo(source); if (!directory.Exists) { // See comment at top of ArchiveAction Class return; } if (!Directory.Exists(destination)) { Directory.CreateDirectory(destination); } FileInfo[] files = directory.GetFiles(); foreach (FileInfo file in files) { if (!excludes.Any(p => p == file.FullName)) { string temppath = Path.Combine(destination, file.Name); file.CopyTo(temppath, true); } } if (copySubDirectories) { DirectoryInfo[] subDirectories = directory.GetDirectories(); foreach (DirectoryInfo subDirectory in subDirectories) { if (!excludes.Any(p => p == subDirectory.FullName)) { string temppath = Path.Combine(destination, subDirectory.Name); DirectoryCopy(subDirectory.FullName, temppath, copySubDirectories); } } } } } } SIDE NOTE: Everthing is moved to a temp directory first because I was having trouble with the zip method and the DirectoryCopy method not copying a directory directly in the source path root.
Example: assuming "C:\Test" is the source and "D:\Backup" is the destination and "C:\Test\directory" has a file in it called "test.txt" the output would look like "D:\Backup\test.txt" instead of "D:\Backup\directory\test.txt", however any directories inside of "C:\Test\directory" would be copied over correctly.
System.IO.Packaginginstead ofSystem.IO.Compressionbecause this is being developed on .NET 4.0 instead of .NET 4.5 \$\endgroup\$Assetclass which definesbackuprTempDirectory. The name of the app is backupr. \$\endgroup\$