• Skip to main content
  • Skip to primary sidebar
  • Skip to secondary sidebar
  • Skip to footer

Coding Still

  • Home
  • About

CORS upload to Amazon S3 from an ASP.NET application

July 18, 2014 By _tasos Leave a Comment

With CORS, you can securely upload files directly into an S3 bucket, directly from the browser, without using an intermediate proxy (e.g. your web server). This option is a huge boost for web applications that handle big files. The alternative is to upload the file to the web server, and then the web server to send it to S3.

In this article we will see

  1. How to configure a S3 bucket so that it can accept requests from our domain
  2. How to implement a web form that will post the file to the S3 bucket
  3. How to upload a file with Ajax for a richer user experience

1. CORS Configuration Manifest

In order for S3 to accept HTTP requests from your domain you need to create a CORS configuration manifest that will declare which domain is allowed to make requests and which actions (PUT, POST, GET, DELETE) will be accepted.

To create the manifest [1]:

  • Choose the S3 option from your AWS Management Console
  • On the bucket you wish to grant CORS access to, click the properties option
  • Note the option, Edit CORS Configuration
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>www.codingstill.com</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>HEAD</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Keep in mind here that if you need to include the port number if your domain runs in a port other than 80, a common case when working with Visual Studio.

2. Web form that performs the upload

Having configured our S3 bucket, we can go on and create our web form that will upload the file directly to our bucket. Here we will need a AWS Access Key ID and a AWS Secret Key. These can be created from the AWS management console. Make sure that these credentials have write access to your bucket.

<%@ Page Title="" Language="C#" AutoEventWireup="true" CodeBehind="awsupload.aspx.cs" Inherits="CodingStillDemo.awsupload%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
    <head>
        <title>S3 POST Form</title> 
        <meta charset="utf-8" />
    </head>
    <body> 
        <form action="https://codingstillcom.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
            <input type="hidden" name="key" value="uploads/<%=FileName=%>">
            <input type="hidden" name="AWSAccessKeyId" value="<%=AwsAccessKeyID=%>"> 
            <input type="hidden" name="acl" value="private"> 
            <input type="hidden" name="success_action_redirect" value="http://localhost/">
            <input type="hidden" name="policy" value="<%=Policy=%>">
            <input type="hidden" name="signature" value="<%=Signature=%>">
            <input type="hidden" name="Content-Type" value="image/jpeg">
            <!-- Include any additional input fields here -->
            File to upload to S3: 
            <input name="file" type="file"> 
            <br> 
            <input type="submit" value="Upload File to S3"> 
        </form> 
    </body>
</html>

This form is from [2] which also explains what every field in the form is. One important thing is the the “file” field must be the last one in the form, since S3 will ignore any values after the actual file.

The article also explains how you can create a policy and a signature that are required in the web form, but not in C# (which is probably the reason you are reading this post!).

Here you will see a JSON representation of a policy that we will use in the web form. You can set its expiration date (currently it set at 2015/01/01) and a few conditions that we will be matched by S3 in order to accept the POST.

{"expiration": "2015-01-01T00:00:00Z",
  "conditions": [ 
    {"bucket": "codingstillcom"}, 
    ["starts-with", "$key", "uploads/"],
    {"acl": "private"},
    ["starts-with", "$Content-Type", ""],
    ["content-length-range", 0, 1048576]
  ]
}

Here is the C# code that converts the JSON policy into a base64 encoded string and also the calculated signature based on your AWS secret key.

public string AwsAccessKeyID
{
    get
    {
        return "MY_AWS_ACCESS_KEY_ID";
    }
}
 
private string AwsSecretKey
{
    get
    {
        return "MY_AWS_SECRET_KEY";
    }
}
 
private string myPolicy
{
    get
    {
        return "{\"expiration\": \"2015-01-01T00:00:00Z\"," + 
                "\"conditions\": [" + "{\"bucket\": \"codingstillcom\"}," + 
                "[\"starts-with\", \"$key\", \"uploads/\"]," + 
                "{\"acl\": \"public-read\"}," + 
                "[\"starts-with\", \"$Content-Type\", \"\"]," + 
                "[\"content-length-range\", 0, 5368709120]" + "]" + "}";
    }
}
 
public string Policy
{
    get
    {
        return Convert.ToBase64String(Encoding.ASCII.GetBytes((myPolicy));
    }
}
 
public string Signature
{
    get
    {
        return GetS3Signature(myPolicy);
    }
}
 
public string GetS3Signature(string policyStr)
{
    string b64Policy = Convert.ToBase64String(Encoding.ASCII.GetBytes(policyStr));
 
    byte[] b64Key = Encoding.ASCII.GetBytes(AwsSecretKey);
    HMACSHA1 hmacSha1 = new HMACSHA1(b64Key);
 
    return Convert.ToBase64String(hmacSha1.ComputeHash(Encoding.ASCII.GetBytes(b64Policy)));
}

The above code successfully posts a file in a S3 bucket. It is really simple, but the downside with this is that you cannot show the progress and if it is a really big file (e.g. video) the user will have to look at the page and wait until the upload completes.

3. CORS upload with Ajax

In this section we will see how we can make the POST to S3 with JavaScript. This approach will give us the option to upload multiple files and display progress to the user while the user can continue and work the page.
The JavaScript code below is based on the sample found in [1] and added some handlers for a better user experience.

//Getting the values from C#
var access_key = '<%=AccessKey%>';
var policy = '<%=Policy%>';
var signature = '<%=Signature%>';
 
function uploadFailed(evt) {
    //message user that the upload failed
}
 
function uploadCanceled(evt) {
    //message user that the upload was canceled
}
 
function uploadProgress(evt) {
    if (evt.lengthComputable) {
        // upload progress is being calculated as a percentage and stored in a variable
        var percentComplete = Math.round(evt.loaded * 100 / evt.total);
    }
}
 
function uploadComplete(evt) {
    //message user that the upload was completed
}
 
// The 'file' parameter is one item from the 'files' array of an input[type="file"] element.
function upload_file(file) {
    var fd = new FormData();
 
    var key = "uploads/" + file.name;
 
    fd.append('key', key);
    fd.append('AWSAccessKeyId', access_key);
    fd.append('acl', 'private');
    fd.append('policy', policy)
    fd.append('signature', signature);
    fd.append('Content-Type', file.type);
    fd.append("file", file);
 
    var xhr = new XMLHttpRequest();
 
    xhr.upload.addEventListener("progress", uploadProgress, false);
    xhr.addEventListener("load", uploadComplete, false);
    xhr.addEventListener("error", uploadFailed, false);
    xhr.addEventListener("abort", uploadCanceled, false);
 
    xhr.open('POST', 'https://codingstillcom.s3.amazonaws.com/', true); //MUST BE LAST LINE BEFORE YOU SEND 
 
    xhr.send(fd);
}

While trying this code open FireBug and go to the “Network” tab. There you might see before the POST request that the browser executes an OPTIONS request. This request is executed so that the browser can check if the POST is valid and will be accepted by the server.

References:

  • [1] Browser-Side Amazon S3 Uploads, Using CORS
  • [2] Browser Uploads to S3 using HTML POST Forms

Filed Under: .NET Development, ASP.NET, JavaScript Tagged With: ASP.NET MVC, ASP.NET Web Forms, AWS, HTML5

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Categories

  • .NET Development
  • ASP.NET
  • Databases
  • Fun
  • IIS
  • JavaScript
  • Web Development

Tags

.NET Core Android ANTLR ASP.NET Ajax ASP.NET Core ASP.NET MVC ASP.NET Web Forms AWS Bouncy Castle Chartjs cli Client info detection Comic Continuous integration CSS Data backup Date handling Firebase Firefox addons Github HigLabo HTML5 Image manipulation jQuery JWT MySQL Nodejs Nuget OAuth Objectionjs OOP openssl Oracle ORM PHP Regular expressions SEO Social media SQL SQL Server UI/UX Url rewriting Videos Visual Studio Web design

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Secondary Sidebar

Archives

  • July 2020
  • March 2020
  • August 2019
  • December 2018
  • November 2018
  • February 2018
  • August 2016
  • June 2016
  • May 2016
  • February 2016
  • January 2016
  • August 2015
  • July 2015
  • October 2014
  • July 2014
  • November 2013
  • April 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • August 2012
  • May 2012
  • February 2012
  • December 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010

Footer

Recent Posts

  • Anatomy of an Objection.js model
  • Check your RSA private and public keys
  • Round functions on the Nth digit
  • Send FCM Notifications in C#
  • Jwt Manager
  • Things around the web #5
  • Query JSON data as relational in MySQL
  • Create and sign JWT token with RS256 using the private key
  • Drop all database objects in Oracle
  • Create and deploy a Nuget package

Latest tweets

  • Things I Wished More Developers Knew About Databases https://t.co/h4gfq6NJgo #softwaredevelopment #databases May 3, 2020 12:52
  • How a Few Lines of Code Broke Lots of Packages https://t.co/p7ZSiLY5ca #javascript May 3, 2020 12:48
  • Can someone steal my IP address and use it as their own? https://t.co/HoQ7Z3BG69 January 24, 2020 13:27
  • Organizational complexity is the best predictor of bugs in a software module https://t.co/aUYn9hD4oa #softwaredevelopment January 13, 2020 08:24
  • Capitalism for people. Socialism for companies? https://t.co/zgUoPcqnix January 5, 2020 07:46
  • http://twitter.com/codingstill

Misc Links

  • George Liatsos Blog
  • Plethora Themes
  • C# / VB Converter
  • Higlabo: .NET library for mail, DropBox, Twitter & more

Connect with me

  • GitHub
  • LinkedIn
  • RSS
  • Twitter
  • Stack Overflow

Copyright © 2021 · eleven40 Pro on Genesis Framework · WordPress · Log in