Amazon S3 Bucket Management with C#: Part 4 – Uploading a file to a Bucket

Before getting started

Skill Level: Beginner

Assumptions:

  1. You already gone through Parts 1, 2, and 3 of Managing Amazon AWS with C#.

Additional information: I sometimes cover small sub-topics in a post. Along with AWS, you will also be exposed to:

  • async, await, Task
  • Rhyous.SimpleArgs
  • Dependency Inject (D in S.O.L.I.D.)
  • Reflection
  • Custom or Common pattern

Step 1 – Add an UploadFile method to BucketManager.cs

  1. Edit file called BucketManager.cs.
  2. Enter this new method:
            public static async Task UploadFile(TransferUtility transferUtility, string bucketName, string file)
            {
                await Task.Run(() => transferUtility.Upload(file, bucketName));
            }
    

    Notice 1: This method has different parameters. We are going to have to fix Program.cs later. Up until now, all of our methods had the same parameters.

  3. Notice 2: The content of this method is an action we would like to run asynchronously, but it is not asynchronous. Task.Run is a static method that runs any method you call inside it asynchronously.

Step 2 – Update the Action Argument

We now need to make this method a valid action for the Action Argument.

    1. Edit the ArgsHandler.cs file to define an Action argument.
                          ...
                          AllowedValues = new ObservableCollection<string>
                          {
                              "CreateBucket",
                              "DeleteBucket",
                              "ListFiles",
                              "UploadFile",
                          },
                          ...
      

Step 3 – Add a File Argument

If we are going to upload a file, we should know which file it is.

  1.                 new Argument
                    {
                        Name = "File",
                        ShortName = "f",
                        Description = "The file.",
                        Example = "{name}=c:\\some\file.txt",
                        CustomValidation = (value) =>
                        {
                            return File.Exists(value);
                        },
                        Action = (value) =>
                        {
                            Console.WriteLine(value);
                        }
                    }
    

    Notice: One of the cool features of SimpleArgs is the ability to declare custom validation. We don’t have to check elsewhere in our code if the file exists. We can use that to validate whether the File parameter is valid or not.

Step 4 – Fix the parameter mismatch problem

To be nice and S.O.L.I.D., our program now needs to determine the method’s dependencies and inject them into the method. It needs to do this dynamically at runtime.

Well, with only one exception, it is most easy to use the Custom or Common pattern. The Custom or Common pattern simply means checking for a customization and if no customization exists, use the common implementation.

To implement this, we could just use a simple IF condition. But just to demonstrate how a Dictionary can be used for the Custom or Common pattern, I will use it.

    class Program
    {
        static void Main(string[] args)
        {
            new ArgsManager<ArgsHandler>().Start(args);
        }

        static Dictionary<string, object[]> CustomParameters = new Dictionary<string, object[]>();

        internal static void OnArgumentsHandled()
        {
            var action = Args.Value("Action");
            var bucketName = Args.Value("Bucket");
            var file = Args.Value("File");

            var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
            MethodInfo mi = typeof(BucketManager).GetMethod(action, flags);

            var region = RegionEndpoint.GetBySystemName(ConfigurationManager.AppSettings["AWSRegion"]);
            var s3client = new AmazonS3Client(region);
            var transferUtility = new TransferUtility(region);

            // Use the Custom or Common pattern
            CustomParameters.Add("UploadFile", new object[] { transferUtility, bucketName, file });
            object[] parameters;
            if (!CustomParameters.TryGetValue(action, out parameters))
                parameters = new object[] { s3client, bucketName };

            var task = mi.Invoke(null, parameters) as Task;
            task.Wait();
        }
    }

Notice in line 8 we create a dictionary. In line 24 we populate the dictionary with a customization. In Lines 26 and 27, we try to get a custom parameter array and if we don’t find it, we use the default one.

Homework: What if every method had different parameters? What would you do?

Go to: Part 5 – Creating a Directory in a Bucket

Return to: Managing Amazon AWS with C#

Leave a Reply

How to post code in comments?