} // proc ZipFileItem private void GZipFileItem(CmdletNotify notify, CmdletProgress bar, Stream src, DirectoryInfo targetPath, FileIndexItem item) { using (var dst = new FileWrite(notify, new FileInfo(Path.Combine(targetPath.FullName, item.ArchiveName)), false, CompressMode.Auto)) { Stuff.CopyRawBytes(bar, item.RelativePath, src.Length, src, dst.Stream); dst.Commit(); } } // proc GZipFileItem
} // proc ReadIndex public void WriteIndex(CmdletNotify notify, string fileName) { var file = CreateIndexFileName(fileName); // erzeuge den index nicht direkt using (var dst = new FileWrite(notify, file, false, CompressMode.Auto)) using (var sw = new StreamWriter(dst.Stream, Encoding.Unicode)) { var csv = new CsvWriter(sw, FileIndexItem.csvDefinition); foreach (var cur in files.Values) { csv.WriteData(cur.GetLineData()); } dst.Commit(); } } // proc WriteIndex
protected override void ProcessRecord() { const string zipArchivePlaceHolder = "#"; using (var bar = Notify.CreateStatus("Erzeuge Backup", $"Sicherung von {Source}...")) { var totalBytes = 0L; //var position = 0L; var itemsModified = 0; var itemsUnmodified = 0; var itemsZipped = 0; var archiveUsed = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase); // Lade Index-Datei bar.StatusDescription = "Lese Index..."; var targetPath = new DirectoryInfo(Target); var targetIndex = Path.Combine(targetPath.FullName, "index.txt.gz"); var index = new FileIndex(); if (String.IsNullOrEmpty(ShadowIndex)) // Kein lokaler Index, also lade dem vom Target { index.ReadIndex(Notify, targetIndex); } else { index.ReadIndex(Notify, ShadowIndex); } // Gleiche die Daten ab und erzeuge die Statistik bar.StatusDescription = "Vergleiche Dateien mit Index..."; var swFileStopWatch = Stopwatch.StartNew(); var files = new FileList(Notify, new DirectoryInfo(Source), Excludes); foreach (var c in files) { var indexItem = index.UpdateFile(c); var tmp = 0; // Gib einen zwischen Bericht if (swFileStopWatch.ElapsedMilliseconds > 500) { bar.StatusDescription = $"Vergleiche {c.RelativePath} mit Index..."; swFileStopWatch = Stopwatch.StartNew(); } switch (indexItem.State) { case FileIndexState.Modified: // Erzeuge den Eintrag im Index if (c.FileInfo.Length < ZipArchiveBorder) { itemsZipped++; indexItem.ArchiveName = zipArchivePlaceHolder; } else { if (String.IsNullOrEmpty(indexItem.ArchiveName)) { indexItem.ArchiveName = Guid.NewGuid().ToString("N") + Path.GetExtension(indexItem.RelativePath) + (ZipFile(indexItem.RelativePath) ? ".gz" : ".nopack"); } } // Statistik für den Progress totalBytes += c.FileInfo.Length; itemsModified++; // Erhöhe den Zugriff if (archiveUsed.TryGetValue(indexItem.ArchiveName, out tmp)) { archiveUsed[indexItem.ArchiveName] = tmp + 1; } break; case FileIndexState.Unmodified: // Prüfe die existens den Archives if (Force || (String.IsNullOrEmpty(ShadowIndex) && !File.Exists(Path.Combine(targetPath.FullName, indexItem.ArchiveName)))) { indexItem.Update(c.FileInfo); goto case FileIndexState.Modified; } itemsUnmodified++; // Erhöhe den Zugriff if (archiveUsed.TryGetValue(indexItem.ArchiveName, out tmp)) { archiveUsed[indexItem.ArchiveName] = tmp + 1; } break; case FileIndexState.None: if (archiveUsed.ContainsKey(indexItem.ArchiveName)) { archiveUsed[indexItem.ArchiveName] = 0; } break; } } // Schreibe das neue Archiv if (itemsModified > 0) { string currentArchiveName = null; FileWrite zipStream = null; ZipOutputStream zip = null; try { bar.StartRemaining(); bar.Maximum = totalBytes; var removeItems = new List <FileIndexItem>(); foreach (var c in index) { switch (c.State) { case FileIndexState.Modified: // Kopiere die Datei using (var src = Stuff.OpenRead(new FileInfo(Path.Combine(Source, c.RelativePath)), Notify, allowEmpty: true)) { if (c.ArchiveName == zipArchivePlaceHolder) { // Schließe das Archiv, wenn genug Inhalt if (zipStream != null && zipStream.Stream.Position > 512 << 20) { zipStream.Commit(); CloseZipStream(zipStream, zip); currentArchiveName = null; } // Erzeuge das Archiv if (currentArchiveName == null) { currentArchiveName = Guid.NewGuid().ToString("N") + ".zip"; CreateZipStream(targetPath, currentArchiveName, out zipStream, out zip); } // Kopiere die Daten ZipFileItem(Notify, bar, src, zip, c); c.ArchiveName = currentArchiveName; } else { GZipFileItem(Notify, bar, src, targetPath, c); } } break; case FileIndexState.None: // Lösche den Index removeItems.Remove(c); break; } } // Entferne die Einträge aus dem Index foreach (var c in removeItems) { index.RemoveEntry(c); } if (zipStream != null) { zipStream.Commit(); } } finally { CloseZipStream(zipStream, zip); } // Schreibe den Index bar.StopRemaining(); bar.StatusDescription = "Schreibe Index..."; if (!String.IsNullOrEmpty(ShadowIndex)) { index.WriteIndex(Notify, ShadowIndex); } index.WriteIndex(Notify, targetIndex); // Lösche ungenutzte Archive if (String.IsNullOrEmpty(ShadowIndex)) { foreach (var c in archiveUsed) { if (c.Value == 0) { var file = new FileInfo(Path.Combine(targetPath.FullName, c.Key)); bar.StatusDescription = $"Nicht mehr benötigtes Archiv '{c.Key}'..."; Notify.SafeIO(file.Delete, bar.StatusDescription); } } } else // Erzeuge nur eine Löschdatei { bar.StatusDescription = "Nicht mehr benötigte Archive werden gelöscht..."; using (var sw = new StreamWriter(Stuff.OpenWrite(new FileInfo(Path.Combine(targetPath.FullName, "index_rm.txt")), Notify, CompressMode.Stored))) { sw.BaseStream.Position = sw.BaseStream.Length; foreach (var c in archiveUsed) { if (c.Value == 0) { sw.WriteLine(c.Key); } } } } } } } // proc ProcessRecord