private static async Task executeS3(string[] args, Credentials credentials) { var nArgs = CLIHelper.GetNamedArguments(args); var helper = new S3Helper(credentials); switch (args[1]?.ToLower()) { case "upload-text": { var keyId = nArgs.GetValueOrDefault("key"); var result = await helper.UploadTextAsync( bucketName : nArgs["bucket"], key : nArgs["path"], text : nArgs["text"], keyId : keyId, encoding : Encoding.UTF8); WriteLine($"SUCCESS, Text Saved, Bucket {nArgs["bucket"]}, Path {nArgs["path"]}, Encryption Key {keyId}, ETag: {result}"); } ; break; case "upload-object": { var file = nArgs["input"].ToFileInfo(); if (!file.Exists) { throw new Exception($"Can't upload file '{file}' because it doesn't exists."); } using (var stream = file.OpenRead()) { var keyId = nArgs.GetValueOrDefault("key"); var result = await helper.UploadStreamAsync( bucketName : nArgs["bucket"], key : nArgs["path"], inputStream : stream, keyId : keyId); WriteLine($"SUCCESS, File was uploaded, result: {result}"); } } ; break; case "upload-folder": { var bucket = nArgs["bucket"]; var path = nArgs["path"]; var directory = nArgs["input"].ToDirectoryInfo(); var excludeFiles = nArgs.GetValueOrDefault("exclude-files", "") .EscapedSplit(',').Where(x => !x.IsNullOrWhitespace()).Select(x => x.ToFileInfo()).ToArray(); var excludeDirectories = nArgs.GetValueOrDefault("exclude-directories", "") .EscapedSplit(',').Where(x => !x.IsNullOrWhitespace()).Select(x => x.ToDirectoryInfo()).ToArray(); var excludePatterns = nArgs.GetValueOrDefault("exclude-file-patterns", "") .EscapedSplit(',').Where(x => !x.IsNullOrWhitespace()).ToArray(); var includePatterns = nArgs.GetValueOrDefault("include-file-patterns", "*") .EscapedSplit(',').Where(x => !x.IsNullOrWhitespace()).ToArray(); var recursive = nArgs.GetValueOrDefault("recursive").ToBoolOrDefault(); //default false var force = nArgs.GetValueOrDefault("force").ToBoolOrDefault(true); //upload even if exists if (!directory.Exists) { throw new Exception($"Can't upload directory '{directory?.FullName}' because it doesn't exists."); } var files = directory.GetFiles(recursive: recursive, inclusivePatterns: includePatterns, exclusivePatterns: excludePatterns); var directories = directory.GetDirectories(recursive: recursive).Merge(directory); if (files.IsNullOrEmpty()) { WriteLine($"No files were found in directory '{directory?.FullName}'"); } if (directories.IsNullOrEmpty()) { WriteLine($"No sub-directories were found in directory '{directory?.FullName}'"); } var prefix = directory.FullName; int uploadedFiles = 0; int uploadedDirectories = 0; WriteLine("Uploading Files..."); files.ParallelForEach(file => { if (excludeFiles.Any(x => x.FullName == file.FullName) || excludeDirectories.Any(x => file.HasSubDirectory(x))) { WriteLine($"Skipping following File Upload due to exclude-files/directories parameter: '{file.FullName}'"); return; } var destination = (path.StartsWith("/") ? path.TrimStart("/") : path).TrimEnd("/") + "/" + file.FullName.TrimStartSingle(prefix).TrimStart('/', '\\').Replace("\\", "/"); destination = destination.TrimStart("/"); //in case path was null or '/' WriteLine($"Uploading '{file.FullName}' => '{bucket}/{destination}' ..."); using (var stream = file.OpenRead()) { var result = helper.UploadStreamAsync( bucketName: bucket, key: destination, inputStream: stream, keyId: null, throwIfAlreadyExists: !force).Result; ++uploadedFiles; WriteLine($"SUCCESS, File '{destination}' was uploaded, result: {result}"); } }); if (nArgs.GetValueOrDefault("create-directories").ToBoolOrDefault(false)) { WriteLine("Creating Directories..."); directories.ParallelForEach(dir => { if (excludeDirectories.Any(x => x.FullName == dir.FullName) || excludeDirectories.Any(x => dir.HasSubDirectory(x))) { WriteLine($"Skipping following Directory Creation due to exclude-files/directories parameter: '{dir.FullName}'"); return; } var destination = (path.StartsWith("/") ? path.TrimStart("/") : path).TrimEnd("/") + "/" + dir.FullName.TrimStartSingle(prefix).Trim('/', '\\').Replace("\\", "/"); destination = destination.TrimStart("/"); //in case path was null or '/' if (destination.IsNullOrEmpty() || helper.ObjectExistsAsync(bucket, key: destination + "/").Result) { WriteLine($"Directory '{destination}' already exists or is an empty destination."); return; } helper.CreateDirectory(bucketName: bucket, path: destination).Await(); ++uploadedDirectories; WriteLine($"Created empty directory '{destination}'."); }); } WriteLine($"SUCCESS, uploaded {uploadedFiles} files and {uploadedDirectories} directories."); } ; break; case "download-text": { var result = await helper.DownloadTextAsync( bucketName : nArgs["bucket"], key : nArgs["path"], eTag : nArgs.GetValueOrDefault("etag"), version : nArgs.GetValueOrDefault("version"), throwIfNotFound : nArgs.GetValueOrDefault("throw-if-not-found").ToBoolOrDefault(true), encoding : Encoding.UTF8); WriteLine($"SUCCESS, Text Read, Bucket: {nArgs["bucket"]}, Path: {nArgs.GetValueOrDefault("path")}, Version: {nArgs.GetValueOrDefault("version")}, eTag: {nArgs.GetValueOrDefault("etag")}, Read: {result?.Length ?? 0} [characters], Result:"); Console.WriteLine(result ?? ""); } ; break; case "delete-object": { var bucket = nArgs["bucket"]; var path = nArgs["path"]; var result = await helper.DeleteVersionedObjectAsync( bucketName : bucket, key : path, throwOnFailure : nArgs.GetValueOrDefault("throw-on-failure").ToBoolOrDefault(true)); if (!result && nArgs.GetValueOrDefault("throw-if-not-deleted").ToBoolOrDefault(false)) { throw new Exception($"File was NOT deleted, Bucket: {bucket}, Path: {path}"); } WriteLine($"SUCCESS, Text Read, Bucket: {bucket}, Path: {path}, Deleted: '{(result ? "true" : "false")}'"); } ; break; case "object-exists": { var throwIfNotFound = nArgs.GetValueOrDefault("throw-if-not-found").ToBoolOrDefault(false); var exists = await helper.ObjectExistsAsync( bucketName : nArgs["bucket"], key : nArgs["path"]); if (!exists && throwIfNotFound) { throw new Exception($"File Does NOT exists, Bucket: {nArgs["bucket"]}, Path: {nArgs["path"]}"); } WriteLine($"SUCCESS, Object Exists Check, Bucket: {nArgs["bucket"]}, Path: {nArgs["path"]}, Exists: {(exists ? "true" : "false")}"); if (!throwIfNotFound) { Console.WriteLine(exists); } } ; break; case "download-object": { var bucket = nArgs["bucket"]; var path = nArgs["path"]; var output = nArgs["output"]; if (Directory.Exists(output)) { output = Path.Combine(output, path.Contains("/") ? path.SplitByLast('/')[1] : path); } WriteLine($"Started Download '{bucket}' -> '{output}'..."); var result = await helper.DownloadObjectAsync( bucketName : nArgs["bucket"], key : path, eTag : nArgs.GetValueOrDefault("etag"), version : nArgs.GetValueOrDefault("version"), outputFile : output, @override : nArgs.GetValueOrDefault("override").ToBoolOrDefault(false)); WriteLine($"SUCCESS, Text Read, Bucket: {bucket}, Path: {path}, Version: {nArgs.GetValueOrDefault("version")}, eTag: {nArgs.GetValueOrDefault("etag")}, Read: {result?.Length ?? 0} [B], Result: {result}"); } ; break; case "download-folder": { var bucket = nArgs["bucket"]; var path = nArgs["path"].TrimEnd('/') + "/"; //path must contain '/' var output = nArgs["output"].ToDirectoryInfo(); var @override = nArgs.GetValueOrDefault("override").ToBoolOrDefault(); var recursive = nArgs.GetValueOrDefault("recursive").ToBoolOrDefault(false); var exclude = nArgs.GetValueOrDefault("exclude", "") .EscapedSplit(',').Where(x => !(x?.TrimStart('/')).IsNullOrWhitespace()).Select(x => x.TrimStart('/')).ToArray(); if (!output.Exists) { if (nArgs.GetValueOrDefault("create-output").ToBoolOrDefault(false)) { output.Create(); } else { throw new Exception($"Can't download S3 objects, because output directory '{output?.FullName}' does NOT exists."); } } var list = helper.ListObjectsAsync(bucket, prefix: path == "/" ? null : path).Result; if (list.IsNullOrEmpty()) { WriteLine($"Coudn't find any object in bucket '{bucket}' with prefix '{path}'."); } list.ParallelForEach(o => { var baseKey = o.Key.TrimStart('/').TrimStart(path.Trim('/')).TrimStart('/'); var excludeMatch = exclude?.FirstOrDefault(ex => baseKey.IsWildcardMatch(ex)); if (excludeMatch != null) { WriteLine($"Skipping download of object: '{o.Key}' due to exclude of '{excludeMatch}'."); return; } var destination = Path.Combine(output.FullName, baseKey).ToFileInfo(); var nonRecursivefileName = o.Key.TrimStart('/').TrimStart(path); if (!recursive && nonRecursivefileName.Count("/") > 0) { WriteLine($"Object '{o.Key}' will NOT be downloaded, because processing has non recursive mode."); return; } if (!@override && destination.Exists) { throw new Exception($"Override not allowed and file already fxists: '{destination?.FullName}'"); } if (o.Key.EndsWith("/")) { WriteLine($"Found Directory, not a file: '{o.BucketName}/{o.Key}', no need to download, created direcory '{destination.Directory.FullName}'."); return; } if (!destination.Directory.Exists) { WriteLine($"Creating missing directory '{destination.Directory.FullName}'..."); destination.Directory.Create(); } WriteLine($"Started Download '{bucket}/{o.Key}' -> '{destination?.FullName}'..."); var result = helper.DownloadObjectAsync( bucketName: bucket, key: o.Key, eTag: o.ETag, version: null, outputFile: destination.FullName, @override: @override).Result; WriteLine($"SUCCESS, File '{destination.FullName}' was saved."); }); } ; break; case "delete-folder": { var bucket = nArgs["bucket"]; var path = nArgs["path"].Trim('/') + "/"; var recursive = nArgs.GetValueOrDefault("recursive").ToBoolOrDefault(false); var list = await helper.ListObjectsAsync(bucket, prefix : path); WriteLine($"Found '{list.Length}' objects with '{path}' prefix in bucket '{bucket}'."); var rootPathCount = path.Trim('/').Count("/") + 1; int counter = 0; list.ParallelForEach(o => { if (!recursive && o.Key.Trim('/').Count("/") > rootPathCount) { WriteLine($"Object '{o.Key}' will NOT be removed from bucket '{bucket}', due to NON recursive execution mode."); return; } WriteLine($"Removing '{o.Key}' from bucket '{bucket}'..."); var success = helper.DeleteObjectAsync(bucketName: bucket, key: o.Key, throwOnFailure: true).Result; ++counter; WriteLine($"Sucesfully removed '{o.Key}' from bucket '{bucket}', response: {success}."); }); list = await helper.ListObjectsAsync(bucket, prefix : path); if (list.IsNullOrEmpty() && await helper.ObjectExistsAsync(bucket, key: path)) { WriteLine($"Removing root directory '{path}' from bucket '{bucket}'..."); var success = await helper.DeleteObjectAsync(bucketName : bucket, key : path, throwOnFailure : true); ++counter; WriteLine($"Sucesfully removed root directory '{path}' from bucket '{bucket}', response: {success}."); } WriteLine($"SUCCESS, removed {counter} files."); } ; break; case "hash-download": { var sync = nArgs.GetOrThrow("sync")?.ToDirectoryInfo(); if (sync?.TryCreate() != true) { throw new Exception($"Sync directory '{sync?.FullName ?? "undefined"}' was not found or could not be created."); } var st = new SyncTarget() { id = nArgs.GetValueOrDefault("id", GuidEx.SlimUID()), source = nArgs.GetOrThrow("source"), status = nArgs.GetOrThrow("status"), destination = nArgs.GetOrThrow("destination"), sync = sync.FullName, verbose = nArgs.GetValueOrDefault("verbose").ToBoolOrDefault(true), verify = nArgs.GetValueOrDefault("verify").ToBoolOrDefault(false), profile = nArgs.GetValueOrDefault("profile", ""), parallelism = nArgs.GetValueOrDefault("parallelism").ToIntOrDefault(2), maxTimestamp = nArgs.GetValueOrDefault("maxTimestamp").ToLongOrDefault(20991230121314), minTimestamp = nArgs.GetValueOrDefault("minTimestamp").ToLongOrDefault(0), wipe = nArgs.GetOrThrow("wipe").ToBoolOrDefault(false), compress = nArgs.GetValueOrDefault("compress").ToBoolOrDefault(false), retry = nArgs.GetValueOrDefault("retry").ToIntOrDefault(5), timeout = nArgs.GetValueOrDefault("timeout").ToIntOrDefault(60000), type = SyncTarget.types.download, throwIfSourceNotFound = nArgs.GetValueOrDefault("throwIfSourceNotFound").ToBoolOrDefault(true), }; var s3hs = new S3HashStore(st); var result = await s3hs.Process(); Console.Write(result.JsonSerialize()); } ; break; case "hash-upload": { var sync = nArgs.GetOrThrow("sync")?.ToDirectoryInfo(); if (sync?.TryCreate() != true) { throw new Exception($"Sync directory '{sync?.FullName ?? "undefined"}' was not found or could not be created."); } var st = new SyncTarget() { id = nArgs.GetValueOrDefault("id", GuidEx.SlimUID()), source = nArgs.GetOrThrow("source"), status = nArgs.GetOrThrow("status"), sync = sync.FullName, destination = nArgs.GetOrThrow("destination"), profile = nArgs.GetValueOrDefault("profile", ""), verbose = nArgs.GetValueOrDefault("verbose").ToBoolOrDefault(true), recursive = nArgs.GetOrThrow("recursive").ToBoolOrDefault(false), parallelism = nArgs.GetValueOrDefault("parallelism").ToIntOrDefault(2), retry = nArgs.GetValueOrDefault("retry").ToIntOrDefault(5), rotation = nArgs.GetOrThrow("rotation").ToInt32(), retention = nArgs.GetValueOrDefault("retention").ToIntOrDefault(1), // retention 1 second compress = nArgs.GetValueOrDefault("compress").ToBoolOrDefault(false), timeout = nArgs.GetValueOrDefault("timeout").ToIntOrDefault(180000), // 3 minutes type = SyncTarget.types.upload, throwIfSourceNotFound = nArgs.GetValueOrDefault("throwIfSourceNotFound").ToBoolOrDefault(true), }; var s3hs = new S3HashStore(st); var result = await s3hs.Process(); Console.Write(result.JsonSerialize()); } ; break; case "help": case "--help": case "-help": case "-h": case "h": HelpPrinter($"{args[0]}", "Amazon S3", ("upload-text", "Accepts params: bucket, path, key, text"), ("download-text", "Accepts params: bucket, path, etag (optional), version (optional)"), ("delete-object", "Accepts params: bucket, path"), ("object-exists", "Accepts params: bucket, path"), ("hash-upload", "Accepts params: id (optional-UID), profile, sourc, status, sync, verbose (optional), parallelism (optional), maxTimestamp (optional), minTimestamp (optional), retry (optional), compress (optional:false), wipe, verify (optional:false), timeout (optional: 60000), throwIfSourceNotFound (optional: true)"), ("hash-download", "Accepts params: id (optional), profile, sourc, status, sync, destination, recursive, verbose (optional), parallelism (optional), wipe, timeout (optional: 180000), retention, rotation, compress (optional:false), throwIfSourceNotFound (optional: true)")); break; default: { Console.WriteLine($"Try '{args[0]} help' to find out list of available commands."); throw new Exception($"Unknown S3 command: '{args[0]} {args[1]}'"); } } }