diff --git a/wbackup/CommandParser.cs b/wbackup/CommandParser.cs index 29f4333..af3f55d 100644 --- a/wbackup/CommandParser.cs +++ b/wbackup/CommandParser.cs @@ -5,21 +5,47 @@ namespace wbackup { + + public enum BackupMopde + { + Incremental, + Differencial, + Full + }; + class CommandParser { + public int maxRetryCount = 1000; public string sourceDir; public string destinationDir; + public string logFileName; + + public BackupMopde backupMode = BackupMopde.Full; public CommandParser(string[] args) { - bool isSource = false; ; + bool isSource = false; bool isDestination = false; + bool isLogFile = false; foreach (var arg in args) { if ('-' == arg.First() || '/' == arg.First()) { string command = arg.Substring(1); + if ("full" == command.ToLower()) + { + backupMode = BackupMopde.Full; + } + if ("incremental" == command.ToLower()) + { + backupMode = BackupMopde.Incremental; + } + if ("differencial" == command.ToLower()) + { + backupMode = BackupMopde.Differencial; + } + if ("srce" == command.ToLower()) { isSource = true; @@ -28,6 +54,10 @@ { isDestination = true; } + if ("log" == command.ToLower()) + { + isLogFile = true; + } } else { @@ -42,6 +72,12 @@ destinationDir = arg; isDestination = false; } + + if (isLogFile) + { + logFileName = arg; + isLogFile = false; + } } } } diff --git a/wbackup/Program.cs b/wbackup/Program.cs index 3968e9a..3228883 100644 --- a/wbackup/Program.cs +++ b/wbackup/Program.cs @@ -17,29 +17,155 @@ static void Main(string[] args) { + var sw = new System.Diagnostics.Stopwatch(); + + string[] storedTarget = { ".mov", ".mp4", ".mpg", ".mpeg", ".jpg", ".jpeg", ".png", ".gif", ".zip" }; + param = new CommandParser(args); ConcurrentQueue srceFilesQueue = new ConcurrentQueue(Directory.GetFiles(param.sourceDir, "*.*", SearchOption.AllDirectories)); + string postFixString = DateTime.Now.ToString("yyyyMMddHHmmss"); + + StreamWriter logws = null; + if (param.logFileName != "") + { + logws = File.CreateText(param.logFileName + "-" + postFixString + ".log"); + } + int totalFileCount = srceFilesQueue.Count; + int archiveFileCount = 0; + int exceptionCount = 0; + Mutex consoleLock = new Mutex(); + + sw.Start(); + Parallel.For(0, 4, i => { var nameTrans = new ZipNameTransform(); - var zf = ZipFile.Create(param.destinationDir + "-" + DateTime.Now.ToString("yyyyMMddHHmmss") + "-" + i.ToString("00") + ".zip"); - zf.BeginUpdate(); + + string zipFileName = param.destinationDir + "-" + postFixString + "-" + i.ToString("00") + ".zip"; + var fos = new FileStream(zipFileName, FileMode.Create, FileAccess.Write); + ZipOutputStream zos = new ZipOutputStream(fos); + zos.SetLevel(4); + zos.UseZip64 = UseZip64.On; + + int buffSize = 65536; + byte[][] readBuffer = new byte[2][]; + readBuffer[0] = new byte[buffSize]; + readBuffer[1] = new byte[buffSize]; while (srceFilesQueue.TryDequeue(out string srceFileName)) { - if (File.GetAttributes(srceFileName).HasFlag(FileAttributes.Archive)) + try { - string f = nameTrans.TransformFile(srceFileName); + var attribute = File.GetAttributes(srceFileName); + if (attribute.HasFlag(FileAttributes.Archive) || param.backupMode== BackupMopde.Full) + { + if (consoleLock.WaitOne()) + { + Console.WriteLine("Archive, {0}", srceFileName); + if (logws != null) + logws.WriteLine("Archive, {0}", srceFileName); + consoleLock.ReleaseMutex(); + } - zf.Add(srceFileName, f); + FileInfo fi = new FileInfo(srceFileName); + string f = nameTrans.TransformFile(srceFileName); + ZipEntry zipEntry = new ZipEntry(f); + zipEntry.DateTime = fi.LastWriteTime; + zipEntry.ExternalFileAttributes = (int )fi.Attributes; + zipEntry.Size = fi.Length; + + // 一部の拡張子について、圧縮対象から除外する + int extPos = srceFileName.LastIndexOf('.'); + if (extPos > 0) + { + string extension = srceFileName.Substring(extPos).ToLower(); + foreach (var extMatch in storedTarget) + { + if (extension == extMatch) + { + zipEntry.CompressionMethod = CompressionMethod.Stored; + break; + } + } + } + + var fis = new FileStream(srceFileName, FileMode.Open, FileAccess.Read); + zos.PutNextEntry(zipEntry); + + int buffSelect = 0; + Task len = fis.ReadAsync(readBuffer[buffSelect], 0, buffSize); + while (true) + { + len.Wait(); + if (len.Result <= 0) + break; + Task writeResult = zos.WriteAsync(readBuffer[buffSelect], 0, len.Result); + + buffSelect ^= 1; + len = fis.ReadAsync(readBuffer[buffSelect], 0, buffSize); + + writeResult.Wait(); + } + fis.Close(); + zos.Flush(); + + if (param.backupMode == BackupMopde.Full || param.backupMode == BackupMopde.Differencial) + { + File.SetAttributes(srceFileName, (attribute & (~FileAttributes.Archive))); + } + + Interlocked.Increment(ref archiveFileCount); + } + else + { + if (consoleLock.WaitOne()) + { + Console.WriteLine("Skip, {0}", srceFileName); + if (logws != null) + logws.WriteLine("Skip, {0}", srceFileName); + consoleLock.ReleaseMutex(); + } + } + } + catch(Exception exp) + { + srceFilesQueue.Enqueue(srceFileName); + + var curtExceptionCnt = Interlocked.Increment(ref exceptionCount); + + if (consoleLock.WaitOne()) + { + Console.WriteLine("Exception {0}, {1}", curtExceptionCnt, exp.ToString()); + if (logws != null) + logws.WriteLine("Exception {0}, {1}", curtExceptionCnt, exp.ToString()); + consoleLock.ReleaseMutex(); + } + + if (curtExceptionCnt > param.maxRetryCount) + break; } } - zf.CommitUpdate(); + zos.Finish(); + zos.Close(); }); - Console.WriteLine("Hello World!"); + sw.Stop(); + Console.WriteLine("TotalMilliseconds = {0}", sw.Elapsed.TotalMilliseconds); + + Console.WriteLine("Finished {0} archived, {1} skipped, {2} aborted.", archiveFileCount, totalFileCount - archiveFileCount, srceFilesQueue.Count); + if (logws != null) + { + logws.Flush(); + logws.WriteLine("Finished {0} archived, {1} skipped, {2} aborted.", archiveFileCount, totalFileCount - archiveFileCount, srceFilesQueue.Count); + + foreach (var fileName in srceFilesQueue) + { + logws.WriteLine("aborted, {0}", fileName); + } + logws.Close(); + } } } } diff --git a/wbackup/Properties/launchSettings.json b/wbackup/Properties/launchSettings.json index cc18adf..9459861 100644 --- a/wbackup/Properties/launchSettings.json +++ b/wbackup/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "wbackup": { "commandName": "Project", - "commandLineArgs": "-srce \"D:\\win32_11gR2_client\" -dest \"C:\\CLWORK\\archive\"" + "commandLineArgs": "-srce \"D:\\win32_11gR2_client\" -dest \"C:\\CLWORK\\archive\\archive\" -log \"C:\\CLWORK\\archive\\archive\" -full" } } } \ No newline at end of file