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.Packaging
instead ofSystem.IO.Compression
because this is being developed on .NET 4.0 instead of .NET 4.5 \$\endgroup\$Asset
class which definesbackuprTempDirectory
. The name of the app is backupr. \$\endgroup\$