nawlrus
nawlrus

Reputation: 797

AWS S3 transferutility Upload never uploads but shows complete without error

I am getting a lot of files that are not being uploaded to my AWS bucket but it the code below always shows complete. Anyone know whats up with this? How am I supposed to find out if the upload failed or not if it always comes back complete? I did see something about using transferutility.EndUpload but I am unsure on how to use it. Also how would I implement a retry? Pass the object state to BeginUpload? Any help?

public class S3Upload
{
    private string awsAccessKeyId = "XXXXXX";
    private string awsSecretAccessKey = "XXXXXX";
    private string bucketName = System.Configuration.ConfigurationManager.AppSettings.Get("BucketName");
    private Amazon.S3.Transfer.TransferUtility transferUtility;
    private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public S3Upload()
    {
        // Initialize log4net.
        log4net.Config.XmlConfigurator.Configure();
        this.transferUtility = new Amazon.S3.Transfer.TransferUtility(awsAccessKeyId, awsSecretAccessKey);
        log.Info("S3 instance initiated");

    }

    public void UploadFile(string filePath, string toPath)
    {

        try
        {
            AsyncCallback callback = new AsyncCallback(uploadComplete);

            log.Info("S3 upload started...");
            log.InfoFormat("S3 filePath: {0}", filePath);
            log.InfoFormat("S3 toPath: {0}", toPath);


            var uploadRequest = new Amazon.S3.Transfer.TransferUtilityUploadRequest();
            uploadRequest.FilePath = filePath;
            uploadRequest.BucketName = bucketName;
            uploadRequest.Key = toPath;
            uploadRequest.StorageClass = Amazon.S3.Model.S3StorageClass.ReducedRedundancy;
            uploadRequest.AddHeader("x-amz-acl", "public-read");
            transferUtility.BeginUpload(uploadRequest, callback, null);
        }
        catch (AmazonS3Exception amazonS3Exception)
        {
              log.ErrorFormat("An Error, number {0}, occurred when creating a bucket with the message '{1}", amazonS3Exception.ErrorCode, amazonS3Exception.Message);    
        }
    }

    private void uploadComplete(IAsyncResult result)
    {
        var x = result;

        if (x.IsCompleted)
        {
            log.Info("S3 upload completed...");

        }
    }
}

Upvotes: 5

Views: 7508

Answers (2)

Richard
Richard

Reputation: 6344

The BeginUpload function starts a new thread that initiates and performs the upload. Calling EndUpload causes the current thread to block until the upload is complete. Also, the EndUpload function catches any exceptions that you'll get from the upload.

If you're going to call BeginUpload, you need to call EndUpload() from a separate thread so that you're not blocking your main thread. If you plan on blocking your main thread, just call the Upload() function instead of the APM versions.

For example, spawn a new thread that will sit and wait for the upload to complete. (Note that it doesn't have to do anything specific, just catch the errors.)

public void UploadFile(string filePath, string toPath)
{

    try
    {
        AsyncCallback callback = new AsyncCallback(uploadComplete);

        log.Info("S3 upload started...");
        log.InfoFormat("S3 filePath: {0}", filePath);
        log.InfoFormat("S3 toPath: {0}", toPath);


        var uploadRequest = new Amazon.S3.Transfer.TransferUtilityUploadRequest();
        uploadRequest.FilePath = filePath;
        uploadRequest.BucketName = bucketName;
        uploadRequest.Key = toPath;
        uploadRequest.StorageClass = Amazon.S3.Model.S3StorageClass.ReducedRedundancy;
        uploadRequest.AddHeader("x-amz-acl", "public-read");
        IAsyncResult ar = transferUtility.BeginUpload(uploadRequest, callback, null);
        // transferUtility.EndUpload(ar);
        ThreadPool.QueueUserWorkItem(c =>
        {
             try
             {
                 transferUtility.EndUpload(ar);
             }
             catch (AmazonS3Exception ex)
             {
                 // Retry, log the error, etc.
             }
             catch (WebException ex)
             {
                 // Retry, log the error, etc.
             }
         });
    }
    catch (AmazonS3Exception amazonS3Exception)
    {
          log.ErrorFormat("An Error, number {0}, occurred when creating a bucket with the message '{1}", amazonS3Exception.ErrorCode, amazonS3Exception.Message);
    }
}

Also, that thread checks for WebException. I don't know if you can get one of these from the Beginupload or not, but some code I'm working on seems to watch for those. (Better safe then sorry.)

Also, I found you can test this with the CurrPorts Utility. That utility lets you kill the open port to amazon (although it will retry up until its configured MaxRetries).

Upvotes: 2

nawlrus
nawlrus

Reputation: 797

Found a fix! I added transferUtility.EndUpload(ar) and if it error's it will write the exception to my log and retry the put request.

Error making request PutObject. System.IO.IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host. --->

public class S3Upload
{
    private string awsAccessKeyId = "XXXXX";
    private string awsSecretAccessKey = "XXXXX";
    private string bucketName = System.Configuration.ConfigurationManager.AppSettings.Get("BucketName");
    private Amazon.S3.Transfer.TransferUtility transferUtility;
    private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public S3Upload()
    {
        // Initialize log4net.
        log4net.Config.XmlConfigurator.Configure();
        this.transferUtility = new Amazon.S3.Transfer.TransferUtility(awsAccessKeyId, awsSecretAccessKey);
        log.Info("S3 instance initiated");

    }

    public void UploadFile(string filePath, string toPath)
    {

        try
        {
            AsyncCallback callback = new AsyncCallback(uploadComplete);

            log.Info("S3 upload started...");
            log.InfoFormat("S3 filePath: {0}", filePath);
            log.InfoFormat("S3 toPath: {0}", toPath);


            var uploadRequest = new Amazon.S3.Transfer.TransferUtilityUploadRequest();
            uploadRequest.FilePath = filePath;
            uploadRequest.BucketName = bucketName;
            uploadRequest.Key = toPath;
            uploadRequest.StorageClass = Amazon.S3.Model.S3StorageClass.ReducedRedundancy;
            uploadRequest.AddHeader("x-amz-acl", "public-read");
            IAsyncResult ar = transferUtility.BeginUpload(uploadRequest, callback, null);
            transferUtility.EndUpload(ar);
        }
        catch (AmazonS3Exception amazonS3Exception)
        {
              log.ErrorFormat("An Error, number {0}, occurred when creating a bucket with the message '{1}", amazonS3Exception.ErrorCode, amazonS3Exception.Message);
        }
    }

    private void uploadComplete(IAsyncResult result)
    {
        var x = result;
    }
}

Upvotes: 2

Related Questions