Click here to Skip to main content
15,895,667 members
Articles / Programming Languages / C#

Amazon S3 Sync

Rate me:
Please Sign up or sign in to vote.
4.92/5 (6 votes)
1 Dec 2010Apache5 min read 48K   1.6K   23  
Synchronize files from your computer to Amazon S3.
using System;
using System.Collections.Generic;
using System.Text;

namespace SyncFromLocalToS3
{
    class Program
    {
        private static SprightlySoftAWS.S3.UploadEstimate MyUpload;
        private static Boolean ShowUploadProgress;
        private static int ExitCode;
        private static String EmailLogData;
        private static int LogLevel;
        private static String LogFilePath;
        private static String SoureFolder;

        private static int EmailLogCondition;
        private static String EmailSmtpHost;
        private static int EmailSmtpPort;
        private static Boolean EmailSmtpEnableSsl;
        private static String EmailSmtpUserName;
        private static String EmailSmtpPassword;
        private static String EmailFromAddress;
        private static String EmailRecipients;


        static void Main(string[] args)
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

            ExitCode = 0;
            EmailLogData = "";
            String S3FolderDelimiter = "/";

            Boolean LogOnlyMode = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "LogOnlyMode"));
            LogLevel = Convert.ToInt32(GetValueFromConfigOrArgument(args, "LogLevel"));
            LogFilePath = GetValueFromConfigOrArgument(args, "LogFilePath");
            Boolean OverwriteExistingLogFile = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "OverwriteExistingLogFile"));

            String AWSAccessKeyId = GetValueFromConfigOrArgument(args, "AWSAccessKeyId");
            String AWSSecretAccessKey = GetValueFromConfigOrArgument(args, "AWSSecretAccessKey");
            Boolean UseSSL = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "UseSSL"));
            String RequestEndpoint = GetValueFromConfigOrArgument(args, "RequestEndpoint");
            String BucketName = GetValueFromConfigOrArgument(args, "BucketName");
            String UploadPrefix = GetValueFromConfigOrArgument(args, "UploadPrefix");
            String UploadHeaders = GetValueFromConfigOrArgument(args, "UploadHeaders");
            String OverrideContentTypes = GetValueFromConfigOrArgument(args, "OverrideContentTypes");
            Boolean CalculateMD5ForUpload = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "CalculateMD5ForUpload"));
            Boolean SaveTimestampsInMetadata = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "SaveTimestampsInMetadata"));
            Single UploadSpeedLimitKBps = Convert.ToSingle(GetValueFromConfigOrArgument(args, "UploadSpeedLimitKBps"));
            ShowUploadProgress = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "ShowUploadProgress"));
            int S3ErrorRetries = Convert.ToInt32(GetValueFromConfigOrArgument(args, "S3ErrorRetries"));

            SoureFolder = GetValueFromConfigOrArgument(args, "SoureFolder");
            Boolean IncludeSubFolders = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "IncludeSubFolders"));
            String ExcludeFolders = GetValueFromConfigOrArgument(args, "ExcludeFolders");
            String IncludeOnlyFilesRegularExpression = GetValueFromConfigOrArgument(args, "IncludeOnlyFilesRegularExpression");
            String ExcludeFilesRegularExpression = GetValueFromConfigOrArgument(args, "ExcludeFilesRegularExpression");

            String CompareFilesBy = GetValueFromConfigOrArgument(args, "CompareFilesBy");
            Boolean DeleteS3ItemsWhereNotInLocalList = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "DeleteS3ItemsWhereNotInLocalList"));

            EmailLogCondition = Convert.ToInt32(GetValueFromConfigOrArgument(args, "EmailLogCondition"));
            EmailSmtpHost = GetValueFromConfigOrArgument(args, "EmailSmtpHost");
            EmailSmtpPort = Convert.ToInt32(GetValueFromConfigOrArgument(args, "EmailSmtpPort"));
            EmailSmtpEnableSsl = Convert.ToBoolean(GetValueFromConfigOrArgument(args, "EmailSmtpEnableSsl"));
            EmailSmtpUserName = GetValueFromConfigOrArgument(args, "EmailSmtpUserName");
            EmailSmtpPassword = GetValueFromConfigOrArgument(args, "EmailSmtpPassword");
            EmailFromAddress = GetValueFromConfigOrArgument(args, "EmailFromAddress");
            EmailRecipients = GetValueFromConfigOrArgument(args, "EmailRecipients");

            //Initialize the log file.
            InitializeLogFile(OverwriteExistingLogFile);


            WriteToLog("");
            WriteToLog("Program started.   " + DateTime.Now);

            WriteToLog("", 2);
            WriteToLog("Input parameters:", 2);
            WriteToLog("   AWSAccessKeyId=" + AWSAccessKeyId, 2);
            WriteToLog("   AWSSecretAccessKey=" + AWSSecretAccessKey, 2);
            WriteToLog("   UseSSL=" + UseSSL, 2);
            WriteToLog("   RequestEndpoint=" + RequestEndpoint, 2);
            WriteToLog("   BucketName=" + BucketName, 2);
            WriteToLog("   UploadPrefix=" + UploadPrefix, 2);
            WriteToLog("   OverrideContentTypes=" + OverrideContentTypes, 2);
            WriteToLog("   CalculateMD5ForUpload=" + CalculateMD5ForUpload, 2);
            WriteToLog("   SaveTimestampsInMetadata=" + SaveTimestampsInMetadata, 2);
            WriteToLog("   UploadSpeedLimitKBps=" + UploadSpeedLimitKBps, 2);
            WriteToLog("   ShowUploadProgress=" + ShowUploadProgress, 2);
            WriteToLog("   S3ErrorRetries=" + S3ErrorRetries, 2);

            WriteToLog("   SoureFolder=" + SoureFolder, 2);
            WriteToLog("   IncludeSubFolders=" + IncludeSubFolders, 2);
            WriteToLog("   ExcludeFolders=" + ExcludeFolders, 2);
            WriteToLog("   IncludeOnlyFilesRegularExpression=" + IncludeOnlyFilesRegularExpression, 2);
            WriteToLog("   ExcludeFilesRegularExpression=" + ExcludeFilesRegularExpression, 2);

            WriteToLog("   CompareFilesBy=" + CompareFilesBy, 2);
            WriteToLog("   DeleteS3ItemsWhereNotInLocalList=" + DeleteS3ItemsWhereNotInLocalList, 2);

            WriteToLog("   LogOnlyMode=" + LogOnlyMode, 2);
            WriteToLog("   LogLevel=" + LogLevel, 2);
            WriteToLog("   LogFilePath=" + LogFilePath, 2);
            WriteToLog("   OverwriteExistingLogFile=" + OverwriteExistingLogFile, 2);

            WriteToLog("   EmailLogCondition=" + EmailLogCondition, 2);
            WriteToLog("   EmailSmtpHost=" + EmailSmtpHost, 2);
            WriteToLog("   EmailSmtpPort=" + EmailSmtpPort, 2);
            WriteToLog("   EmailSmtpEnableSsl=" + EmailSmtpEnableSsl, 2);
            WriteToLog("   EmailSmtpUserName=" + EmailSmtpUserName, 2);
            WriteToLog("   EmailSmtpPassword=" + EmailSmtpPassword, 2);
            WriteToLog("   EmailFromAddress=" + EmailFromAddress, 2);
            WriteToLog("   EmailRecipients=" + EmailRecipients, 2);


            //Check that the input values are valid.
            if (AWSAccessKeyId == "")
            {
                WriteToLog("Error, the AWSAccessKeyId is empty.  Set it in the App.config file or pass it in the command line.");
                ExitCode = 1;
            }
            else if (AWSSecretAccessKey == "")
            {
                WriteToLog("Error, the AWSSecretAccessKey is empty.  Set it in the App.config file or pass it in the command line.");
                ExitCode = 1;
            }
            else if (BucketName == "")
            {
                WriteToLog("Error, the BucketName is empty.  Set it in the App.config file or pass it in the command line.");
                ExitCode = 1;
            }
            else if (SoureFolder == "")
            {
                WriteToLog("Error, the SoureFolder is empty.  Set it in the App.config file or pass it in the command line.");
                ExitCode = 1;
            }
            else if (System.IO.Directory.Exists(SoureFolder) == false)
            {
                WriteToLog("Error, the SoureFolder does not exist.  Specify a valif path.  SoureFolder=" + SoureFolder);
                ExitCode = 1;
            }
            else if (UploadPrefix != "" && UploadPrefix.EndsWith(S3FolderDelimiter) == false)
            {
                WriteToLog("Error, the UploadPrefix is the S3 folder to sync with.  It must end with the S3FolderDelimiter value.  UploadPrefix=" + UploadPrefix + "   S3FolderDelimiter=" + S3FolderDelimiter);
                ExitCode = 1;
            }
            else if (UploadPrefix != "" && GetS3FileExists(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, UploadPrefix, S3ErrorRetries) != 200)
            {
                WriteToLog("Error, the UploadPrefix folder does not exist on S3.  UploadPrefix=" + UploadPrefix);
                ExitCode = 1;
            }


            if (ExitCode == 0)
            {
                //Make sure source path ends with the backslash character.
                if (SoureFolder.EndsWith("\\") == false)
                {
                    SoureFolder += "\\";
                }

                MyUpload = new SprightlySoftAWS.S3.UploadEstimate();
                MyUpload.ProgressChangedEvent += MyUpload_ProgressChangedEvent;


                //Convert the user's UploadHeaders into a dictionary.
                Dictionary<String, String> UserRequestHeaders;
                UserRequestHeaders = BuildDictionaryFromString(UploadHeaders);

                //Convert the user's OverrideContentTypes into a dictionary.
                Dictionary<String, String> UserContentTypes;
                UserContentTypes = BuildDictionaryFromString(OverrideContentTypes);


                //Build an ArrayList of the user's folders to exclude.
                System.Collections.ArrayList ExcludeFolderslArrayList = new System.Collections.ArrayList();
                if (ExcludeFolders != "")
                {
                    System.IO.DirectoryInfo MyDirectoryInfo;

                    String[] ExcludeLocalArray = ExcludeFolders.Split(new Char[] { '|' });
                    foreach (String ArrayValue in ExcludeLocalArray)
                    {
                        if (System.IO.Directory.Exists(ArrayValue) == true)
                        {
                            MyDirectoryInfo = new System.IO.DirectoryInfo(ArrayValue);
                            ExcludeFolderslArrayList.Add(MyDirectoryInfo.FullName.ToLower());
                        }
                    }
                }


                System.Collections.Hashtable S3HashTable = new System.Collections.Hashtable();
                System.Collections.ArrayList LocalArrayList = new System.Collections.ArrayList();

                Boolean RetBool;

                //List all items on S3 and put them in a HashTable.
                WriteToLog("", 2);
                WriteToLog("Getting files on S3.");
                RetBool = PopulateS3HashTable(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, UploadPrefix, ref S3HashTable);
                WriteToLog("   Number of items in S3 list: " + S3HashTable.Count);

                if (RetBool == true)
                {
                    //List all local items to sync and put them in an ArrayList.
                    WriteToLog("");
                    WriteToLog("Getting local files.");
                    PopulateLocalArrayList(SoureFolder, SoureFolder, IncludeSubFolders, ExcludeFolderslArrayList, IncludeOnlyFilesRegularExpression, ExcludeFilesRegularExpression, ref LocalArrayList);
                    WriteToLog("   Number of items in local list: " + LocalArrayList.Count);


                    //Create an ArrayList of S3 items that do not exist in the LocalArrayList. 
                    System.Collections.ArrayList DeleteS3ArrayList = new System.Collections.ArrayList();
                    PopulateDeleteS3ArrayList(ref S3HashTable, ref LocalArrayList, UploadPrefix, S3FolderDelimiter, SoureFolder, ref DeleteS3ArrayList);

                    //Use a Dictionary to store files to upload.  We may be calculating the ETag hash and we can pass these values to the upload function in the dictionary.
                    Dictionary<String, String> UploadDictionary = new Dictionary<String, String>();
                    PopulateUploadDictionary(ref S3HashTable, ref LocalArrayList, UploadPrefix, S3FolderDelimiter, SoureFolder, CompareFilesBy, ref UploadDictionary);


                    if (LogOnlyMode == true)
                    {
                        //List what would be changed on S3.
                        if (DeleteS3ItemsWhereNotInLocalList == true)
                        {
                            //List items that would be deleted on S3.
                            WriteToLog("");
                            WriteToLog("The following items would be deleted on S3:");

                            foreach (String DeleteItem in DeleteS3ArrayList)
                            {
                                WriteToLog("   " + DeleteItem);
                            }
                            WriteToLog("   Number of items that would be deleted: " + DeleteS3ArrayList.Count);
                        }

                        //List items that would be uploaded to S3.
                        WriteToLog("");
                        WriteToLog("The following items would be uploaded to S3:");

                        foreach (KeyValuePair<String, String> UploadKeyValuePair in UploadDictionary)
                        {
                            WriteToLog("   " + UploadKeyValuePair.Key);
                        }
                        WriteToLog("   Number of items that would be uploaded: " + UploadDictionary.Count);
                    
                    }
                    else
                    {
                        //Make the changes on S3.

                        if (DeleteS3ItemsWhereNotInLocalList == true)
                        {
                            //Process delete queue.
                            WriteToLog("");
                            WriteToLog("Deleting items on S3:");
                            DeleteExtraOnS3(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, ref DeleteS3ArrayList, S3ErrorRetries);
                        }

                        //Process upload queue.
                        WriteToLog("");
                        WriteToLog("Uploading items on S3:");
                        UploadMissingToS3(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, UploadPrefix, S3FolderDelimiter, SoureFolder, UserRequestHeaders, UserContentTypes, CalculateMD5ForUpload, ref UploadDictionary, SaveTimestampsInMetadata, UploadSpeedLimitKBps, ShowUploadProgress, S3ErrorRetries);
                    }
                }
            }


            SendEmailLog();

            Environment.ExitCode = ExitCode;

            WriteToLog("");
            WriteToLog("Program complete.   " + DateTime.Now);
        }


        private static Boolean PopulateS3HashTable(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, String UploadPrefix, ref System.Collections.Hashtable S3HashTable)
        {
            Boolean RetBool;
            SprightlySoftAWS.S3.ListBucket MyListBucket = new SprightlySoftAWS.S3.ListBucket();
            RetBool = MyListBucket.ListBucket(UseSSL, RequestEndpoint, BucketName, "", UploadPrefix, AWSAccessKeyId, AWSSecretAccessKey);

            if (RetBool == true)
            {
                foreach (SprightlySoftAWS.S3.ListBucket.BucketItemObject MyBucketItemObject in MyListBucket.BucketItemsArrayList)
                {
                    WriteToLog("   Item added to S3 list.   KeyName=" + MyBucketItemObject.KeyName, 2);
                    S3HashTable.Add(MyBucketItemObject.KeyName, MyBucketItemObject);
                }
            }
            else
            {
                WriteToLog("   Error listing files on S3.   ErrorDescription=" + MyListBucket.ErrorDescription);
                WriteToLog(MyListBucket.LogData, 3);
            }

            return RetBool;
        }


        private static void PopulateLocalArrayList(String BaseFolder, String CurrentFolder, Boolean IncludeSubFolders, System.Collections.ArrayList ExcludeFolderslArrayList, String IncludeOnlyFilesRegularExpression, String ExcludeFilesRegularExpression, ref System.Collections.ArrayList LocalArrayList)
        {
            System.IO.DirectoryInfo CurrentDirectoryInfo;
            CurrentDirectoryInfo = new System.IO.DirectoryInfo(CurrentFolder);

            String FolderName;

            foreach (System.IO.FileInfo MyFileInfo in CurrentDirectoryInfo.GetFiles())
            {
                if (ExcludeFilesRegularExpression != "")
                {
                    //check if the file is excluded
                    if (System.Text.RegularExpressions.Regex.IsMatch(MyFileInfo.FullName, ExcludeFilesRegularExpression, System.Text.RegularExpressions.RegexOptions.IgnoreCase) == true)
                    {
                        WriteToLog("   Local file excluded by ExcludeFilesRegularExpression.   Name=" + MyFileInfo.FullName, 2);
                    }
                    else
                    {
                        if (IncludeOnlyFilesRegularExpression != "")
                        {
                            if (System.Text.RegularExpressions.Regex.IsMatch(MyFileInfo.FullName, IncludeOnlyFilesRegularExpression, System.Text.RegularExpressions.RegexOptions.IgnoreCase) == true)
                            {
                                WriteToLog("   File added to local list.   Name=" + MyFileInfo.FullName, 2);
                                LocalArrayList.Add(MyFileInfo.FullName);
                            }
                            else
                            {
                                WriteToLog("   Local file not included by IncludeOnlyFilesRegularExpression.   Name=" + MyFileInfo.FullName, 2);
                            }
                        }
                        else
                        {
                            WriteToLog("   File added to local list.   Name=" + MyFileInfo.FullName, 2);
                            LocalArrayList.Add(MyFileInfo.FullName);
                        }
                    }
                }
                else
                {
                    if (IncludeOnlyFilesRegularExpression != "")
                    {
                        if (System.Text.RegularExpressions.Regex.IsMatch(MyFileInfo.FullName, IncludeOnlyFilesRegularExpression, System.Text.RegularExpressions.RegexOptions.IgnoreCase) == true)
                        {
                            WriteToLog("   File added to list.   Name=" + MyFileInfo.FullName, 2);
                            LocalArrayList.Add(MyFileInfo.FullName);
                        }
                        else
                        {
                            WriteToLog("   Local file not included by IncludeOnlyFilesRegularExpression.   Name=" + MyFileInfo.FullName, 2);
                        }
                    }
                    else
                    {
                        WriteToLog("   File added to local list.   Name=" + MyFileInfo.FullName, 2);
                        LocalArrayList.Add(MyFileInfo.FullName);
                    }
                }
            }


            if (IncludeSubFolders == true)
            {
                foreach (System.IO.DirectoryInfo SubDirectoryInfo in CurrentDirectoryInfo.GetDirectories())
                {
                    if (ExcludeFolderslArrayList.Contains(SubDirectoryInfo.FullName.ToLower()) == true)
                    {
                        WriteToLog("   Local folder excluded by ExcludeFolders.   Name=" + SubDirectoryInfo.FullName, 2);
                    }
                    else
                    {
                        FolderName = SubDirectoryInfo.FullName;
                        if (FolderName.EndsWith("\\") == false)
                        {
                            FolderName += "\\";
                        }

                        WriteToLog("   Folder added to local list.   Name=" + FolderName, 2);
                        LocalArrayList.Add(FolderName);

                        PopulateLocalArrayList(BaseFolder, SubDirectoryInfo.FullName, IncludeSubFolders, ExcludeFolderslArrayList, IncludeOnlyFilesRegularExpression, ExcludeFilesRegularExpression, ref LocalArrayList);
                    }
                }
            }
            else
            {
                WriteToLog("   Sub folders excluded by IncludeSubFolders.", 2);
            }
        }


        private static void PopulateDeleteS3ArrayList(ref System.Collections.Hashtable S3HashTable, ref System.Collections.ArrayList LocalArrayList, String UploadPrefix, String S3FolderDelimiter, String SoureFolder, ref System.Collections.ArrayList DeleteS3ArrayList)
        {
            //For each S3 item, check if it exists locally.

            String KeyName;
            String LocalPath;

            foreach (System.Collections.DictionaryEntry MyDictionaryEntry in S3HashTable)
            {
                KeyName = MyDictionaryEntry.Key.ToString();
                KeyName = KeyName.Substring(UploadPrefix.Length);

                LocalPath = System.IO.Path.Combine(SoureFolder, KeyName.Replace(S3FolderDelimiter, "\\"));

                if (LocalArrayList.Contains(LocalPath) == false)
                {
                    DeleteS3ArrayList.Add(MyDictionaryEntry.Key);
                }
            }
        }


        private static void PopulateUploadDictionary(ref System.Collections.Hashtable S3HashTable, ref System.Collections.ArrayList LocalArrayList, String UploadPrefix, String S3FolderDelimiter, String SoureFolder, String CompareFilesBy, ref Dictionary<String, String> UploadDictionary)
        {
            //Check which local items need to be uploaded to S3.

            String LocalPathAsKey;
            SprightlySoftAWS.S3.CalculateHash MyCalculateHash = new SprightlySoftAWS.S3.CalculateHash();
            String LocalETag;
            System.IO.FileInfo MyFileInfo;

            CompareFilesBy = CompareFilesBy.ToLower();

            foreach (String LocalPath in LocalArrayList)
            {
                LocalPathAsKey = LocalPath;
                LocalPathAsKey = LocalPathAsKey.Substring(SoureFolder.Length);
                LocalPathAsKey = LocalPathAsKey.Replace("\\", S3FolderDelimiter);

                LocalPathAsKey = System.IO.Path.Combine(UploadPrefix, LocalPathAsKey);

                if (S3HashTable.ContainsKey(LocalPathAsKey) == true)
                {
                    //Only check files to see if the content is different.
                    if (LocalPath.EndsWith("\\") == false)
                    {
                        //The local file exists on S3.  Check if the files are different.

                        SprightlySoftAWS.S3.ListBucket.BucketItemObject MyBucketItemObject;
                        MyBucketItemObject = S3HashTable[LocalPathAsKey] as SprightlySoftAWS.S3.ListBucket.BucketItemObject;

                        if (CompareFilesBy == "etag")
                        {
                            LocalETag = MyCalculateHash.CalculateETagFromFile(LocalPath);

                            if (LocalETag == MyBucketItemObject.ETag.Replace("\"", ""))
                            {
                                //Files are the same.
                            }
                            else
                            {
                                UploadDictionary.Add(LocalPath, LocalETag);
                            }
                        }
                        else if (CompareFilesBy == "size")
                        {
                            MyFileInfo = new System.IO.FileInfo(LocalPath);

                            if (MyFileInfo.Length == MyBucketItemObject.Size)
                            {
                                //Files are the same.
                            }
                            else
                            {
                                UploadDictionary.Add(LocalPath, "");
                            }
                        }
                        else
                        {
                            //If the FileName is different the file will not exists on S3.  No need to do a check here.
                        }
                    }
                }
                else
                {
                    //The local file does not exist on S3, add it to the upload list.
                    UploadDictionary.Add(LocalPath, "");
                }
            }
        }


        private static void DeleteExtraOnS3(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, ref System.Collections.ArrayList DeleteS3ArrayList, int S3ErrorRetries)
        {
            Boolean RetBool;
            int ErrorNumber = 0;
            String ErrorDescription = "";
            String LogData = "";
            int ResponseStatusCode = 0;
            String ResponseStatusDescription = "";
            Dictionary<String, String> ResponseHeaders = new Dictionary<String, String>();
            String ResponseString = "";
            int DeleteCount = 0;

            foreach (String DeleteItem in DeleteS3ArrayList)
            {
                //Send the correct parameters to the MakeS3Request function to delete a file on S3.  This function will wait and retry if there is a 503 error.
                RetBool = MakeS3Request(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, DeleteItem, "", "DELETE", null, "", S3ErrorRetries, ref ErrorNumber, ref ErrorDescription, ref LogData, ref ResponseStatusCode, ref ResponseStatusDescription, ref ResponseHeaders, ref ResponseString);

                if (RetBool == true)
                {
                    WriteToLog("   Delete S3 file successful.   S3KeyName=" + DeleteItem);
                    DeleteCount += 1;
                }
                else
                {
                    WriteToLog("   Delete S3 file failed.   S3KeyName=" + DeleteItem + "   ErrorNumber=" + ErrorNumber + "   ErrorDescription=" + ErrorDescription + "   ResponseString=" + ResponseString);
                    WriteToLog(LogData, 2);
                    WriteToLog("   Canceling deletion of extra files on S3.");
                    ExitCode = 1;
                    break;
                }
            }

            WriteToLog("   Number of items deleted: " + DeleteCount);
        }


        private static Boolean MakeS3Request(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, String KeyName, String QueryString, String RequestMethod, Dictionary<String, String> ExtraHeaders, String SendData, int RetryTimes, ref int ErrorNumber, ref String ErrorDescription, ref String LogData, ref int ResponseStatusCode, ref String ResponseStatusDescription, ref Dictionary<String, String> ResponseHeaders, ref String ResponseString)
        {
            SprightlySoftAWS.REST MyREST = new SprightlySoftAWS.REST();
            String RequestURL;
            Dictionary<String, String> ExtraRequestHeaders;
            String AuthorizationValue;
            Boolean RetBool = true;
            LogData = "";

            for (int i = 0; i == RetryTimes; i++)
            {
                RequestURL = MyREST.BuildS3RequestURL(UseSSL, RequestEndpoint, BucketName, KeyName, QueryString);

                ExtraRequestHeaders = new Dictionary<String, String>();

                if (ExtraHeaders != null)
                {
                    foreach (KeyValuePair<String, String> MyKeyValuePair in ExtraHeaders)
                    {
                        ExtraRequestHeaders.Add(MyKeyValuePair.Key, MyKeyValuePair.Value);
                    }
                }

                ExtraRequestHeaders.Add("x-amz-date", DateTime.UtcNow.ToString("r"));

                AuthorizationValue = MyREST.GetS3AuthorizationValue(RequestURL, RequestMethod, ExtraRequestHeaders, AWSAccessKeyId, AWSSecretAccessKey);
                ExtraRequestHeaders.Add("Authorization", AuthorizationValue);

                RetBool = MyREST.MakeRequest(RequestURL, RequestMethod, ExtraRequestHeaders, SendData);

                //Set the return values.
                ErrorNumber = MyREST.ErrorNumber;
                ErrorDescription = MyREST.ErrorDescription;
                LogData += MyREST.LogData;
                ResponseStatusCode = MyREST.ResponseStatusCode;
                ResponseStatusDescription = MyREST.ResponseStatusDescription;
                ResponseHeaders = MyREST.ResponseHeaders;
                ResponseString = MyREST.ResponseString;

                if (RetBool == true)
                {
                    break;
                }
                else
                {
                    if (MyREST.ResponseStatusCode == 503)
                    {
                        //A Service Unavailable response was returned.  Wait and retry.
                        System.Threading.Thread.Sleep(1000 * i * i);
                    }
                    else if (MyREST.ErrorNumber == 1003)
                    {
                        //Getting the response failed.  This may be a network disconnection.  Wait and retry.
                        System.Threading.Thread.Sleep(1000 * i * i);
                    }
                    else
                    {
                        //An error occured but retrying would not solve the problem.
                        break;
                    }
                }
            }

            return RetBool;
        }


        private static void UploadMissingToS3(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, String UploadPrefix, String S3FolderDelimiter, String SoureFolder, Dictionary<String, String> UserRequestHeaders, Dictionary<String, String> UserContentTypes, Boolean CalculateMD5ForUpload, ref Dictionary<String, String> UploadDictionary, Boolean SaveTimestampsInMetadata, Single UploadSpeedLimitKBps, Boolean ShowUploadProgress, int S3ErrorRetries)
        {
            Boolean RetBool;
            int ErrorNumber = 0;
            String ErrorDescription = "";
            String LogData = "";
            int ResponseStatusCode = 0;
            String ResponseStatusDescription = "";
            Dictionary<String, String> ResponseHeaders = new Dictionary<String, String>();
            String ResponseString = "";

            Dictionary<String, String> ExtraHeaders = new Dictionary<String, String>();
            String DiffName;
            String LocalMD5Hash;
            String MyExtension;
            String KeyName;
            System.IO.FileInfo MyFileInfo;
            System.IO.DirectoryInfo MyDirectoryInfo;
            int UploadCount = 0;

            SprightlySoftAWS.S3.CalculateHash MyCalculateHash = new SprightlySoftAWS.S3.CalculateHash();
            SprightlySoftAWS.S3.Helper MyS3Helper = new SprightlySoftAWS.S3.Helper();

            //Get a dictionary of content types from the SprightlySoftAWS.S3.Helper class.
            Dictionary<String, String> ContentTypesDictionary;
            ContentTypesDictionary = MyS3Helper.GetContentTypesDictionary();


            foreach (KeyValuePair<String, String> UploadKeyValuePair in UploadDictionary)
            {
                DiffName = UploadKeyValuePair.Key.Substring(SoureFolder.Length, UploadKeyValuePair.Key.Length - SoureFolder.Length);
                DiffName = DiffName.Replace("\\", "/");

                KeyName = UploadPrefix + DiffName;

                if (UploadKeyValuePair.Key.EndsWith("\\") == true)
                {
                    ExtraHeaders = new Dictionary<String, String>();
                    if (SaveTimestampsInMetadata == true)
                    {
                        MyDirectoryInfo = new System.IO.DirectoryInfo(UploadKeyValuePair.Key);
                        ExtraHeaders.Add("x-amz-meta-local-date-created", MyDirectoryInfo.CreationTime.ToFileTimeUtc().ToString());
                    }

                    RetBool = MakeS3Request(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, KeyName, "", "PUT", ExtraHeaders, "", S3ErrorRetries, ref ErrorNumber, ref ErrorDescription, ref LogData, ref ResponseStatusCode, ref ResponseStatusDescription, ref ResponseHeaders, ref ResponseString);

                    if (RetBool == true)
                    {
                        WriteToLog("   Create S3 folder successful.   S3KeyName=" + KeyName);
                        UploadCount += 1;
                    }
                    else
                    {
                        WriteToLog("   Create S3 folder failed.   S3KeyName=" + KeyName + "   ErrorNumber=" + ErrorNumber + "   ErrorDescription=" + ErrorDescription + "   ResponseString=" + ResponseString);
                        WriteToLog(LogData, 2);
                        WriteToLog("   Canceling upload of missing files to S3.");
                        ExitCode = 1;
                        break;
                    }
                }
                else
                {
                    //Calculate the MD5 for upload if required.
                    //Set the content type.
                    //Add extra headers.

                    ExtraHeaders = new Dictionary<String, String>();

                    if (CalculateMD5ForUpload == true)
                    {
                        if (UploadKeyValuePair.Value == "")
                        {
                            //Calculate the MD5.
                            LocalMD5Hash = MyCalculateHash.CalculateMD5FromFile(UploadKeyValuePair.Key);
                        }
                        else
                        {
                            //Convert the ETag to MD5.
                            LocalMD5Hash = MyS3Helper.ConvertETagToMD5(UploadKeyValuePair.Value);
                        }

                        ExtraHeaders.Add("Content-MD5", LocalMD5Hash);
                    }


                    MyExtension = System.IO.Path.GetExtension(UploadKeyValuePair.Key).ToLower();

                    if (UserContentTypes.ContainsKey(MyExtension) == true)
                    {
                        ExtraHeaders.Add("Content-Type", UserContentTypes[MyExtension]);
                    }
                    else if (ContentTypesDictionary.ContainsKey(MyExtension) == true)
                    {
                        ExtraHeaders.Add("Content-Type", ContentTypesDictionary[MyExtension]);
                    }


                    if (SaveTimestampsInMetadata == true)
                    {
                        MyFileInfo = new System.IO.FileInfo(UploadKeyValuePair.Key);
                        ExtraHeaders.Add("x-amz-meta-local-date-modified", MyFileInfo.LastWriteTimeUtc.ToFileTimeUtc().ToString());
                        ExtraHeaders.Add("x-amz-meta-local-date-created", MyFileInfo.CreationTime.ToFileTimeUtc().ToString());
                    }


                    //Add the user supplied headers to the other headers that will be sent to S3.
                    foreach (KeyValuePair<String, String> MyKeyValuePair in UserRequestHeaders)
                    {
                        ExtraHeaders.Add(MyKeyValuePair.Key, MyKeyValuePair.Value);
                    }


                    WriteToLog("   Uploading file.   S3KeyName=" + KeyName);
                    RetBool = UploadFileToS3(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, KeyName, "PUT", ExtraHeaders, UploadKeyValuePair.Key, UploadSpeedLimitKBps, S3ErrorRetries, ref ErrorNumber, ref ErrorDescription, ref LogData, ref ResponseStatusCode, ref ResponseStatusDescription, ref ResponseHeaders, ref ResponseString);

                    if (RetBool == true)
                    {
                        WriteToLog("      Upload file to S3 was successful.");
                        UploadCount += 1;
                    }
                    else
                    {
                        WriteToLog("      Upload file to S3 failed.   ErrorNumber=" + ErrorNumber + "   ErrorDescription=" + ErrorDescription + "   ResponseString=" + ResponseString);
                        WriteToLog(LogData, 2);
                        WriteToLog("   Canceling upload of missing files to S3.");
                        ExitCode = 1;
                        break;
                    }
                }
            }

            WriteToLog("   Number of items uploaded: " + UploadCount);
        }


        private static Boolean UploadFileToS3(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, String KeyName, String RequestMethod, Dictionary<String, String> ExtraHeaders, String LocalFileName, Single UploadSpeedLimitKBps, int RetryTimes, ref int ErrorNumber, ref String ErrorDescription, ref String LogData, ref int ResponseStatusCode, ref String ResponseStatusDescription, ref Dictionary<String, String> ResponseHeaders, ref String ResponseString)
        {
            String RequestURL;
            Dictionary<String, String> ExtraRequestHeaders;
            String AuthorizationValue;
            Boolean RetBool = true;
            LogData = "";

            if (UploadSpeedLimitKBps > 0)
            {
                MyUpload.LimitKBpsSpeed = UploadSpeedLimitKBps;
            }


            for (int i = 0; i == RetryTimes; i++)
            {
                RequestURL = MyUpload.BuildS3RequestURL(UseSSL, RequestEndpoint, BucketName, KeyName, "");

                ExtraRequestHeaders = new Dictionary<String, String>();

                if (ExtraHeaders != null)
                {
                    foreach (KeyValuePair<String, String> MyKeyValuePair in ExtraHeaders)
                    {
                        ExtraRequestHeaders.Add(MyKeyValuePair.Key, MyKeyValuePair.Value);
                    }
                }

                ExtraRequestHeaders.Add("x-amz-date", DateTime.UtcNow.ToString("r"));

                AuthorizationValue = MyUpload.GetS3AuthorizationValue(RequestURL, RequestMethod, ExtraRequestHeaders, AWSAccessKeyId, AWSSecretAccessKey);
                ExtraRequestHeaders.Add("Authorization", AuthorizationValue);

                RetBool = MyUpload.UploadFile(RequestURL, RequestMethod, ExtraRequestHeaders, LocalFileName);

                //Set the return values.
                ErrorNumber = MyUpload.ErrorNumber;
                ErrorDescription = MyUpload.ErrorDescription;
                LogData += MyUpload.LogData;
                ResponseStatusCode = MyUpload.ResponseStatusCode;
                ResponseStatusDescription = MyUpload.ResponseStatusDescription;
                ResponseHeaders = MyUpload.ResponseHeaders;
                ResponseString = MyUpload.ResponseString;

                if (RetBool == true)
                {
                    break;
                }
                else
                {
                    if (MyUpload.ResponseStatusCode == 503)
                    {
                        //A Service Unavailable response was returned.  Wait and retry.
                        System.Threading.Thread.Sleep(1000 * i * i);
                    }
                    else if (MyUpload.ErrorNumber == 1003)
                    {
                        //Getting the response failed.  This may be a network disconnection.  Wait and retry.
                        System.Threading.Thread.Sleep(1000 * i * i);
                    }
                    else
                    {
                        //An error occured but retrying would not solve the problem.
                        break;
                    }
                }
            }

            return RetBool;
        }
        

        private static int GetS3FileExists(String AWSAccessKeyId, String AWSSecretAccessKey, Boolean UseSSL, String RequestEndpoint, String BucketName, String UploadPrefix, int S3ErrorRetries)
        {
            Boolean RetBool;
            int ErrorNumber = 0;
            String ErrorDescription = "";
            String LogData = "";
            int ResponseStatusCode = 0;
            String ResponseStatusDescription = "";
            Dictionary<String, String> ResponseHeaders = new Dictionary<String, String>();
            String ResponseString = "";

            RetBool = MakeS3Request(AWSAccessKeyId, AWSSecretAccessKey, UseSSL, RequestEndpoint, BucketName, UploadPrefix, "", "HEAD", null, "", S3ErrorRetries, ref ErrorNumber, ref ErrorDescription, ref LogData, ref ResponseStatusCode, ref ResponseStatusDescription, ref ResponseHeaders, ref ResponseString);

            if (RetBool == true)
            {
                return ResponseStatusCode;
            }
            else
            {
                WriteToLog("Error checking if the file exists on S3.   UploadPrefix=" + UploadPrefix + "   ResponseStatusCode=" + ResponseStatusCode + "   ErrorDescription=" + ErrorDescription + "   ResponseString=" + ResponseString);
                return 0;
            }
        }


        private static Dictionary<String, String> BuildDictionaryFromString(String InputString)
        {
            Dictionary<String, String> MyDictionary = new Dictionary<String, String>();

            if (InputString.Contains(":") == true)
            {
                try
                {
                    //Split multiple headers with the comma character.
                    String[] HeadersArray;
                    HeadersArray = InputString.Split(new Char[] { '|' });

                    String[] PairArray;
                    foreach (String ArrayValue in HeadersArray)
                    {
                        //Split the name and value with the colon character.
                        PairArray = ArrayValue.Split(new Char[] { ':' });

                        MyDictionary.Add(PairArray[0], PairArray[1]);
                    }
                }
                catch (Exception ex)
                {
                    WriteToLog("Error parsing the UploadHeaders.  ex.Message=" + ex.Message);
                }
            }

            return MyDictionary;
        }


        private static String GetValueFromConfigOrArgument(string[] args, String ArgumentName)
        {
            String CurrentValue;
            CurrentValue = System.Configuration.ConfigurationManager.AppSettings[ArgumentName];

            ArgumentName = ArgumentName.ToLower();
            String CurrentArg;

            for (int i = 0; i < args.Length; i++)
            {
                CurrentArg = args[i].ToLower();

                if (CurrentArg.StartsWith("-" + ArgumentName) | CurrentArg.StartsWith("/" + ArgumentName))
                {
                    try
                    {
                        CurrentValue = args[i + 1];
                    }
                    catch (Exception ex)
                    {
                        WriteToLog("Error reading command line value.  ArgumentName=" + ArgumentName + "   ex.Message=" + ex.Message);
                    }

                    break;
                }
            }

            return CurrentValue;
        }
        
        
        private static void MyUpload_ProgressChangedEvent()
        {
            if (ShowUploadProgress == true)
            {
                if (MyUpload.BytesTotal > 0)
                {
                    SprightlySoftAWS.S3.Helper MyHelper = new SprightlySoftAWS.S3.Helper();
                    Double MyDouble = (Convert.ToDouble(MyUpload.BytesTransfered) / Convert.ToDouble(MyUpload.BytesTotal)) * 100;

                    Console.Write(MyDouble.ToString("0.##") + "% complete.   " + MyUpload.CurrentKBpsSpeed.ToString("0.##") + " KB/s   " + MyHelper.FormatByteSize(MyUpload.BytesTransfered) + " / " + MyHelper.FormatByteSize(MyUpload.BytesTotal) + "      \r");
                }
            }
        }


        private static void SendEmailLog()
        {
            //Send the log information in an email.
            if ((EmailLogCondition == 1 & ExitCode != 0) | EmailLogCondition == 2)
            {
                String EmailSubject;

                if (ExitCode == 0)
                {
                    EmailSubject = "SyncFromLocalToS3 Success, " + SoureFolder;
                }
                else
                {
                    EmailSubject = "SyncFromLocalToS3 Failed, " + SoureFolder;
                }

                WriteToLog("");
                WriteToLog("Sending the log email.");
                SendEmail(EmailSmtpHost, EmailSmtpPort, EmailSmtpEnableSsl, EmailSmtpUserName, EmailSmtpPassword, EmailFromAddress, EmailRecipients, EmailSubject, EmailLogData);
            }
        }


        private static void SendEmail(String EmailSmtpHost, int EmailSmtpPort, Boolean EmailSmtpEnableSsl, String EmailSmtpUserName, String EmailSmtpPassword, String EmailFromAddress, String EmailRecipients, String EmailSubject, String EmailBody)
        {
            try
            {
                System.Net.Mail.SmtpClient MySmtpClient = new System.Net.Mail.SmtpClient();
                MySmtpClient.Host = EmailSmtpHost;
                MySmtpClient.Port = EmailSmtpPort;
                MySmtpClient.EnableSsl = EmailSmtpEnableSsl;

                if (EmailSmtpUserName != "")
                {
                    System.Net.NetworkCredential MyNetworkCredential = new System.Net.NetworkCredential();
                    MyNetworkCredential.UserName = EmailSmtpUserName;
                    MyNetworkCredential.Password = EmailSmtpPassword;

                    MySmtpClient.Credentials = MyNetworkCredential;
                }

                System.Net.Mail.MailAddress FromMailAddress = new System.Net.Mail.MailAddress(EmailFromAddress);
                System.Net.Mail.MailAddress ToMailAddress = new System.Net.Mail.MailAddress(EmailRecipients);

                System.Net.Mail.MailMessage MyMailMessage = new System.Net.Mail.MailMessage(FromMailAddress, ToMailAddress);

                MyMailMessage.Subject = EmailSubject;
                MyMailMessage.Body = EmailBody;
                
                MySmtpClient.Send(MyMailMessage);
            }
            catch (Exception ex)
            {
                WriteToLog("Sending the log email failed.   ex.Message=" + ex.Message);
                ExitCode = 1;
            }
        }


        private static void InitializeLogFile(Boolean OverwriteExistingLogFile)
        {
            if (LogFilePath != "")
            {
                String LogLine = "";

                if (OverwriteExistingLogFile == true)
                {
                    try
                    {
                        if (System.IO.File.Exists(LogFilePath) == true)
                        {
                            LogLine = "Deleting the existing log file.   LogFilePath=" + LogFilePath;
                            System.IO.File.Delete(LogFilePath);
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                }

                try
                {
                    System.IO.StreamWriter MyStreamWriter;
                    MyStreamWriter = new System.IO.StreamWriter(LogFilePath, true, System.Text.Encoding.Unicode);
                    MyStreamWriter.Close();

                    if (LogLine != "")
                    {
                        WriteToLog(LogLine);
                    }
                }
                catch (Exception ex)
                {
                    LogLine = "Error opening log file.  No data will be written to the log file.   LogFilePath=" + LogFilePath + "   ex.Message=" + ex.Message;
                    Console.WriteLine(LogLine);
                    EmailLogData += LogLine + Environment.NewLine;
                    LogFilePath = "";
                }
            }
        }


        private static void WriteToLog(String LogLine, int LogLineLevel = 1)
        {
            if (LogLineLevel <= LogLevel)
            {
                Console.WriteLine(LogLine);

                if (LogLevel > 1)
                {
                    LogLine = DateTime.Now + "   " + LogLine;
                }

                if (LogFilePath != "")
                {
                    System.IO.StreamWriter MyStreamWriter;
                    MyStreamWriter = new System.IO.StreamWriter(LogFilePath, true, System.Text.Encoding.Unicode);
                    MyStreamWriter.WriteLine(LogLine);
                    MyStreamWriter.Close();
                }

                EmailLogData += LogLine + Environment.NewLine;
            }
        }


        static void MyHandler(object sender, UnhandledExceptionEventArgs args)
        {
            Exception e = (Exception)args.ExceptionObject;

            WriteToLog("");
            WriteToLog("Error in application.");
            WriteToLog("Message=" + e.Message);
            WriteToLog("StackTrace=" + e.StackTrace);

            ExitCode = 1;

            //Send the log email.
            SendEmailLog();
        }


    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions