1 //-----------------------------------------------------------------------
2 // <copyright file="BackupService.cs" company="Takayoshi Matsuyama">
3 // Copyright (c) Takayoshi Matsuyama. All rights reserved.
5 //-----------------------------------------------------------------------
7 // This file is part of Simple Backup.
9 // Simple Backup is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
14 // Simple Backup is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with Simple Backup. If not, see <http://www.gnu.org/licenses/>.
22 namespace SimpleBackup.Services
25 using System.Collections.Generic;
27 using System.IO.Compression;
30 using System.Text.RegularExpressions;
31 using System.Threading;
32 using SimpleBackup.Core;
33 using SimpleBackup.Core.Models;
34 using SimpleBackup.Core.Services;
35 using SimpleBackup.Core.Utilities;
36 using SimpleBackup.Resources;
41 internal class BackupService : IBackupService
44 /// The synchronize root
46 private static readonly object SyncRoot = new object();
49 /// The instance of BackupService.
51 private static volatile BackupService instance;
54 /// Prevents a default instance of the <see cref="BackupService"/> class from being created.
56 private BackupService()
58 BackupSettingService.Instance.BackupSettingCreated += this.OnBackupSettingCreated;
59 BackupSettingService.Instance.BackupSettingOpened += this.OnBackupSettingOpened;
60 BackupSettingService.Instance.BackupSettingClosed += this.OnBackupSettingClosed;
64 /// Occurs when file backup progress is updated.
66 public event EventHandler<FileBackupProgressEventArgs> NofityFileBackupProgress;
69 /// Gets the instance.
71 public static IBackupService Instance
81 instance = new BackupService();
91 /// Gets a value indicating whether this service is ready for backup.
93 public bool IsReadyForBackup { get; private set; }
96 /// Gets a value indicating whether this service is executing backup.
98 public bool IsExecutingBackup { get; private set; }
101 /// Backups multipul files.
103 /// <param name="cancellationToken">The cancellation token.</param>
104 /// <param name="resultInfos">The array of result information.</param>
105 public void BackupMultipulFiles(CancellationToken cancellationToken, out IResultInfo[] resultInfos)
107 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
109 this.IsExecutingBackup = true;
110 var resultList = new List<IResultInfo>();
111 if (this.IsReadyForBackup == false)
113 resultList.Add(modelFactory.CreateResultInfo(BackupState.Failed, Resources.Backup_Failed_BackupInfomationNotPrepared));
114 resultInfos = resultList.ToArray();
120 this.NotifyBackupProgress(0, null);
122 int processedCount = 0;
124 foreach (IBackupInfo backupInfo in BackupSettingService.Instance.CurrentSetting.BackupInfos)
126 cancellationToken.ThrowIfCancellationRequested();
127 backupInfo.BackupState = BackupState.InProgress;
131 IResultInfo resultInfo = null;
132 double progressValue = 0;
134 if (backupInfo.IsEnabled == false)
136 string description = string.Format(
138 Resources.Backup_Skipped,
139 Resources.Backup_From,
140 backupInfo.SourcePath);
141 resultInfo = modelFactory.CreateResultInfo(BackupState.Skipped, description);
143 progressValue = processedCount * 100.0 / BackupSettingService.Instance.CurrentSetting.Count;
144 this.NotifyBackupProgress(progressValue, resultInfo);
145 backupInfo.BackupState = BackupState.Skipped;
150 if (((backupInfo.BackupSourceType == BackupSourceType.File) && (File.Exists(backupInfo.SourcePath) == false)) ||
151 ((backupInfo.BackupSourceType == BackupSourceType.Folder) && (Directory.Exists(backupInfo.SourcePath) == false)))
153 string description = string.Format(
154 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
155 Resources.Backup_Failed_SourceNotExist,
156 Resources.Backup_From,
157 backupInfo.SourcePath,
159 backupInfo.DestinationFolderPath);
160 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
161 resultList.Add(resultInfo);
163 progressValue = processedCount * 100.0 / BackupSettingService.Instance.CurrentSetting.Count;
164 this.NotifyBackupProgress(progressValue, resultInfo);
165 backupInfo.BackupState = BackupState.Failed;
169 // Check the destination.
170 if ((backupInfo.DestinationFolderPath == "\\") ||
171 (Directory.Exists(backupInfo.DestinationFolderPath) == false))
173 string description = string.Format(
174 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
175 Resources.Backup_Failed_DestinationNotExist,
176 Resources.Backup_From,
177 backupInfo.SourcePath,
179 backupInfo.DestinationFolderPath);
180 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
181 resultList.Add(resultInfo);
183 progressValue = processedCount * 100.0 / BackupSettingService.Instance.CurrentSetting.Count;
184 this.NotifyBackupProgress(progressValue, resultInfo);
185 backupInfo.BackupState = BackupState.Failed;
189 var destinationFileNames = new IDestinationPathInfo[0];
192 if (backupInfo.IsZipArchive)
194 if (backupInfo.BackupSourceType == BackupSourceType.File)
196 BackupFileToZip(backupInfo, out destinationFileNames);
198 else if (backupInfo.BackupSourceType == BackupSourceType.Folder)
200 BackupFolerToZip(backupInfo, out destinationFileNames);
205 if (backupInfo.BackupSourceType == BackupSourceType.File)
207 BackupFile(backupInfo, out destinationFileNames);
209 else if (backupInfo.BackupSourceType == BackupSourceType.Folder)
211 BackupFolder(backupInfo, out destinationFileNames);
217 string description = string.Format(
218 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}\r\n{5}",
219 Resources.Backup_Failed_FailedToCopy,
220 Resources.Backup_From,
221 backupInfo.SourcePath,
223 destinationFileNames.FirstOrDefault(),
225 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
226 resultList.Add(resultInfo);
228 progressValue = processedCount * 100.0 / BackupSettingService.Instance.CurrentSetting.Count;
229 this.NotifyBackupProgress(progressValue, resultInfo);
230 backupInfo.BackupState = BackupState.Failed;
234 if (CheckExistence(destinationFileNames))
236 string description = string.Format(
237 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
238 Resources.Backup_Succeeded,
239 Resources.Backup_From,
240 backupInfo.SourcePath,
242 destinationFileNames.First().Path);
243 resultInfo = modelFactory.CreateResultInfo(BackupState.Success, description);
244 resultList.Add(resultInfo);
245 backupInfo.BackupState = BackupState.Success;
246 backupInfo.LatestSuccessfulBackupTargetName = destinationFileNames.First().Path;
250 progressValue = processedCount * 100.0 / BackupSettingService.Instance.CurrentSetting.Count;
251 this.NotifyBackupProgress(progressValue, resultInfo);
254 this.NotifyBackupProgress(100.0, null);
256 catch (OperationCanceledException)
258 var resultInfo = modelFactory.CreateResultInfo(BackupState.Cancelled, Resources.BackupState_Cancelled);
259 resultList.Add(resultInfo);
260 this.NotifyBackupProgress(100.0, resultInfo);
264 resultInfos = resultList.ToArray();
265 this.IsExecutingBackup = false;
270 /// Backups single file.
272 /// <param name="backupInfoId">The backup information identifier.</param>
273 /// <returns>The result information.</returns>
274 public IResultInfo BackupSingleFile(Guid backupInfoId)
276 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
278 IBackupInfo backupInfo = BackupSettingService.Instance.CurrentSetting.FirstOrDefault(backupInfoId);
279 if (backupInfo == null)
281 return modelFactory.CreateResultInfo(
283 Resources.Backup_Failed_BackupInfomationNotPrepared);
286 backupInfo.BackupState = BackupState.InProgress;
287 Helpers.UpdateWpfGui();
289 IResultInfo resultInfo = null;
290 string description = string.Empty;
293 if (((backupInfo.BackupSourceType == BackupSourceType.File) && (File.Exists(backupInfo.SourcePath) == false)) ||
294 ((backupInfo.BackupSourceType == BackupSourceType.Folder) && (Directory.Exists(backupInfo.SourcePath) == false)))
296 description = string.Format(
297 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
298 Resources.Backup_Failed_SourceNotExist,
299 Resources.Backup_From,
300 backupInfo.SourcePath,
302 backupInfo.DestinationFolderPath);
303 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
304 backupInfo.BackupState = BackupState.Failed;
308 // Check the destination.
309 if (Directory.Exists(backupInfo.DestinationFolderPath) == false)
311 description = string.Format(
312 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
313 Resources.Backup_Failed_DestinationNotExist,
314 Resources.Backup_From,
315 backupInfo.SourcePath,
317 backupInfo.DestinationFolderPath);
318 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
319 backupInfo.BackupState = BackupState.Failed;
323 var destinationFileNames = new IDestinationPathInfo[0];
326 if (backupInfo.IsZipArchive)
328 if (backupInfo.BackupSourceType == BackupSourceType.File)
330 BackupFileToZip(backupInfo, out destinationFileNames);
332 else if (backupInfo.BackupSourceType == BackupSourceType.Folder)
334 BackupFolerToZip(backupInfo, out destinationFileNames);
339 if (backupInfo.BackupSourceType == BackupSourceType.File)
341 BackupFile(backupInfo, out destinationFileNames);
343 else if (backupInfo.BackupSourceType == BackupSourceType.Folder)
345 BackupFolder(backupInfo, out destinationFileNames);
351 description = string.Format(
352 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}\r\n{5}",
353 Resources.Backup_Failed_FailedToCopy,
354 Resources.Backup_From,
355 backupInfo.SourcePath,
357 destinationFileNames.FirstOrDefault(),
359 resultInfo = modelFactory.CreateResultInfo(BackupState.Failed, description);
360 backupInfo.BackupState = BackupState.Failed;
364 if (CheckExistence(destinationFileNames))
366 description = string.Format(
367 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
368 Resources.Backup_Succeeded,
369 Resources.Backup_From,
370 backupInfo.SourcePath,
372 destinationFileNames.First().Path);
373 resultInfo = modelFactory.CreateResultInfo(BackupState.Success, description);
374 backupInfo.BackupState = BackupState.Success;
375 backupInfo.LatestSuccessfulBackupTargetName = destinationFileNames.First().Path;
379 description = string.Format(
380 "{0}\r\n\t{1}: {2}\r\n\t{3}: {4}",
381 Resources.Backup_Failed_FailedToCopy,
382 Resources.Backup_From,
383 backupInfo.SourcePath,
385 destinationFileNames.FirstOrDefault());
386 return modelFactory.CreateResultInfo(BackupState.Failed, description);
389 private static void BackupFileToZip(IBackupInfo backupInfo, out IDestinationPathInfo[] destinationPathInfoArray)
391 var innerDestinationPathInfoList = new List<IDestinationPathInfo>();
393 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
395 Match lastPathSegmentMatch = Constants.EndPathSegmentRegex.Match(backupInfo.SourcePath);
396 Match fileNameMatch = Constants.FileNameRegex.Match(lastPathSegmentMatch.Value);
397 Match extensionMatch = Constants.ExtensionRegex.Match(lastPathSegmentMatch.Value);
399 string destinationFolderPath = backupInfo.DestinationFolderPath.AddTailPathSeparatorIfNotExist();
400 string destinationTempFolderPath = destinationFolderPath + fileNameMatch.Value +
401 " - " + DateTime.Now.ToString("yyyyMMdd-HHmmss") + "-tmp";
402 string destinationTempFileName = destinationTempFolderPath + "\\" + fileNameMatch.Value +
403 " - " + DateTime.Now.ToString("yyyyMMdd-HHmmss") + "." + extensionMatch.Value;
408 Directory.CreateDirectory(destinationTempFolderPath);
412 throw new InvalidOperationException(
414 "Failed to create the folder: {0}\r\n{1}",
415 destinationTempFolderPath,
422 File.Copy(backupInfo.SourcePath, destinationTempFileName);
426 throw new InvalidOperationException(
428 "Failed to copy the file.\r\n\tFrom: {0}\r\n\tTo: {1}\r\n{2}",
429 backupInfo.SourcePath,
430 destinationTempFolderPath,
435 var destinationFileName = destinationFolderPath + fileNameMatch.Value
436 + " - " + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".zip";
437 innerDestinationPathInfoList.Add(modelFactory.CreateDestinationPathInfo(BackupSourceType.File, destinationFileName));
441 ZipFile.CreateFromDirectory(
442 destinationTempFolderPath,
444 CompressionLevel.Optimal,
446 Encoding.GetEncoding(Properties.Settings.Default.DefaultZipEncodingKeyText));
450 backupInfo.BackupState = BackupState.Failed;
451 throw new InvalidOperationException(
453 "Failed to create the zip archive.\r\n\tFrom: {0}\r\n\tTo: {1}\r\n{2}",
454 destinationTempFolderPath,
462 destinationPathInfoArray = innerDestinationPathInfoList.ToArray();
464 if (File.Exists(destinationTempFileName))
466 File.Delete(destinationTempFileName);
469 if (Directory.Exists(destinationTempFolderPath))
471 Directory.Delete(destinationTempFolderPath);
476 private static void BackupFolerToZip(IBackupInfo backupInfo, out IDestinationPathInfo[] destinationPathInfoArray)
478 var innerDestinationPathInfoList = new List<IDestinationPathInfo>();
480 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
482 Match lastPathSegmentMatch = Constants.EndPathSegmentRegex.Match(backupInfo.SourcePath);
484 string destinationFolderPath = backupInfo.DestinationFolderPath.AddTailPathSeparatorIfNotExist();
485 var destinationFileName = destinationFolderPath
486 + lastPathSegmentMatch.Value + " - "
487 + DateTime.Now.ToString("yyyyMMdd-HHmmss")
489 innerDestinationPathInfoList.Add(modelFactory.CreateDestinationPathInfo(BackupSourceType.File, destinationFileName));
493 ZipFile.CreateFromDirectory(
494 backupInfo.SourcePath,
496 CompressionLevel.Optimal,
498 Encoding.GetEncoding(Properties.Settings.Default.DefaultZipEncodingKeyText));
502 backupInfo.BackupState = BackupState.Failed;
503 throw new InvalidOperationException(
505 "Failed to create the zip archive.\r\n\tFrom: {0}\r\n\tTo: {1}\r\n{2}",
506 backupInfo.SourcePath,
513 destinationPathInfoArray = innerDestinationPathInfoList.ToArray();
517 private static void BackupFile(IBackupInfo backupInfo, out IDestinationPathInfo[] destinationPathInfoArray)
519 var innerDestinationPathInfoList = new List<IDestinationPathInfo>();
521 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
523 Match lastPathSegmentMatch = Constants.EndPathSegmentRegex.Match(backupInfo.SourcePath);
524 Match fileNameMatch = Constants.FileNameRegex.Match(lastPathSegmentMatch.Value);
525 Match extensionMatch = Constants.ExtensionRegex.Match(lastPathSegmentMatch.Value);
527 string destinationFolderPath = backupInfo.DestinationFolderPath.AddTailPathSeparatorIfNotExist();
528 var destinationFileName = destinationFolderPath + fileNameMatch.Value
529 + " - " + DateTime.Now.ToString("yyyyMMdd-HHmmss") + "." + extensionMatch.Value;
530 innerDestinationPathInfoList.Add(modelFactory.CreateDestinationPathInfo(BackupSourceType.File, destinationFileName));
534 File.Copy(backupInfo.SourcePath, destinationFileName);
538 destinationPathInfoArray = innerDestinationPathInfoList.ToArray();
542 private static void BackupFolder(IBackupInfo backupInfo, out IDestinationPathInfo[] destinationPathInfoArray)
544 var innerDestinationPathInfoList = new List<IDestinationPathInfo>();
546 var modelFactory = ObjectContainer.Resolve<IModelFactory>();
548 Match lastPathSegmentMatch = Constants.EndPathSegmentRegex.Match(backupInfo.SourcePath);
550 //// 方法 : ディレクトリをコピーする
551 //// http://msdn.microsoft.com/ja-jp/library/bb762914(v=vs.110).aspx
552 string destinationFolderPath = backupInfo.DestinationFolderPath.AddTailPathSeparatorIfNotExist();
553 var destinationFolderName = destinationFolderPath
554 + lastPathSegmentMatch.Value + " - " + DateTime.Now.ToString("yyyyMMdd-HHmmss");
555 innerDestinationPathInfoList.Add(modelFactory.CreateDestinationPathInfo(BackupSourceType.Folder, destinationFolderName));
559 FolderCopy(backupInfo.SourcePath, destinationFolderName, true);
563 destinationPathInfoArray = innerDestinationPathInfoList.ToArray();
567 private static void FolderCopy(string sourceFolderPath, string destinationFolderPath, bool copySubFolders)
569 var sourceDirectoryInfo = new DirectoryInfo(sourceFolderPath);
570 DirectoryInfo[] subDirectoryInfos = sourceDirectoryInfo.GetDirectories();
572 if (sourceDirectoryInfo.Exists == false)
574 throw new DirectoryNotFoundException(
575 "Source directory does not exist or could not be found: "
579 // If the destination directory doesn't exist, create it.
580 if (Directory.Exists(destinationFolderPath) == false)
582 Directory.CreateDirectory(destinationFolderPath);
585 // Get the files in the directory and copy them to the new location.
586 FileInfo[] files = sourceDirectoryInfo.GetFiles();
587 foreach (FileInfo file in files)
589 string temppath = Path.Combine(destinationFolderPath, file.Name);
590 file.CopyTo(temppath, false);
593 // If copying subdirectories, copy them and their contents to new location.
596 foreach (DirectoryInfo subDirectoryInfo in subDirectoryInfos)
598 string temppath = Path.Combine(destinationFolderPath, subDirectoryInfo.Name);
599 FolderCopy(subDirectoryInfo.FullName, temppath, copySubFolders);
604 private static bool CheckExistence(IDestinationPathInfo[] destinationPathInfoArray)
606 return (destinationPathInfoArray != null) &&
607 (destinationPathInfoArray.Length != 0) &&
608 destinationPathInfoArray.All(
609 destinationPathInfo =>
611 if (destinationPathInfo.BackupSourceType == BackupSourceType.File)
613 return File.Exists(destinationPathInfo.Path);
616 return Directory.Exists(destinationPathInfo.Path);
620 private void OnBackupSettingCreated(object sender, SettingFileEventArgs settingFileEventArgs)
622 this.IsReadyForBackup = true;
625 private void OnBackupSettingOpened(object sender, SettingFileEventArgs settingFileEventArgs)
627 this.IsReadyForBackup = true;
630 private void OnBackupSettingClosed(object sender, SettingFileEventArgs settingFileEventArgs)
632 this.IsReadyForBackup = false;
635 private void NotifyBackupProgress(double progressValue, IResultInfo latestResultInfo)
637 if (this.NofityFileBackupProgress != null)
639 this.NofityFileBackupProgress(this, new FileBackupProgressEventArgs(progressValue, latestResultInfo));