S3 Exercise 2.5: Add Objects to a Bucket With Versioning Enabled

Kerry Sheldon
6 min readJun 9, 2018

--

(This post is part of the AWS for the Bootcamp Grad series. The series consists of exercises to build familiarity with various aspects of the AWS ecosystem. Again, all of these posts are “exercises” for introductory exposure to AWS — they are NOT represented as best practices.)

Background

In this exercise, you will create an S3 bucket to store text files. You will configure this bucket to enable versioning, whereby the bucket will maintain multiple variants of each object in that bucket.

You will also create a static website hosted on S3 as an interface to upload files to the bucket, see the contents of the bucket, as well as all versions of each file object. To do that, you will write a browser-side script that uses the AWS SDK, and authentication through Amazon Cognito.

This exercise if very similar to Exercise 2.3 — except that it introduces versioning and requires additional use of the AWS Javascript SDK.

Specifically, you will do the following:

  • Create an S3 bucket to store files.
  • Configure CORS on that bucket.
  • Configure the bucket to enable versioning.
  • Create an Amazon Cognito identity pool (your browser script will need to provide credentials to access your S3 resources).
  • Configure the Amazon Cognito’s IAM role for unauthenticated users to provide access to your S3 bucket.
  • Create another S3 bucket and configure it to host a static website.
  • Create a browser side script to list and display all files in the file bucket, as well as upload a file to that bucket.
  • Create a browser side script to list all versions of each file in that bucket.
  • Create a browser side script to display the text of any selected file version.

Create an S3 bucket to Store Files

Follow the Create an S3 Bucket section of Exercise 2.1.

Configure CORS on the Bucket

Add a CORS configuration to that bucket from the Permissions tab. Select CORS configuration and add the following.

<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
<MaxAgeSeconds>3000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>

This rule will allow GET and PUT requests from any origin, with any header.

Once you configure CORS, you will also need to ensure that this bucket has public read and write permissions. From the Permissions tab, under Public Access, click Everyone, then ensure that List Objects and Write Objects are selected. Click Save.

Configure Bucket to Enable Versioning

Click the Properties tab from the bucket. Click Versioning then Enable Versioning and Save.

Create an Amazon Cognito Identity Pool and Configure IAM Role

Follow the Create an Amazon Cognito Identify Pool and Configure IAM Role section of Exercise 2.3.

Create Another S3 Bucket and Configure it for Website Hosting

Follow the Create an S3 Bucket and Configure the Bucket to Host a Static Website sections of Exercise 2.1.

Create a Browser Side Script to Upload and List All Files

In this exercise, you will be uploading two objects to the website bucket you just created — index.html and app.js. The app.js file will be the browser-side script that performs actions on the S3 bucket containing your images.

Create an index.html file with the following:

<html>
<head>
<!-- Javascript AWS SDK -->
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.247.1.min.js"></script>
<!-- moment.js library - used for date formatting -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<!-- Your Browser-Side script -->
<script src="./app.js"></script>
<!-- Latest compiled and minified Boostrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
.btn-info {margin-right: 10px}
</style>
</head> <body>
<div class="container">
<!-- upload a file -->
<div class="row">
<form>
<div class="form-group">
<label for="upload">Add Text File</label>
<input id="upload" type="file" class="form-control-file" accept=".txt" >
</div>
<button type='button' class="btn btn-info btn-xs" onclick="addFile()">Upload</button>
<form>
</div>
<!-- list of files -->
<div class="row">
<h4>My Files</h4>
<div id="content"></div>
</div>
</div> <script>
//calls a method on your browser-side script - app.js
listFiles();
</script>
</body>
</html>

Create an app.js file with listFiles and addFile functions. These functions will make use of the JavaScript AWS SDK (see the S3 class documentation here):

//insert name of your file bucket
const Bucket = 'YOUR-BUCKET-NAME';
//insert identity pool id for your cognito pool
//you can get this by clicking Edit Identity Pool from your Cognito Pool
const IdentityPoolId = 'YOUR-POOL-ID';
const credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId })//insert region for your image bucket
const region = 'YOUR-REGION'; //e.g. 'us-east-1'
const bucketUrl = `https://s3.amazonaws.com/${Bucket}`//uses the AWS SDK
//updates its configuration with your credentials and bucket region
AWS.config.update({
region,
credentials
});
//uses the AWS SDK
//creates an S3 service object that can interact with your bucket
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: { Bucket }
});
function contentContainer() {
return document.getElementById('content')
}
function clearContent() {
contentContainer().innerHTML = ""
}
//function called from index.html
//gets name of all objects in the s3 bucket
//updates HTML with the file name
function listFiles() {
clearContent()
s3.listObjects({Delimiter: '/'}, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
const files = data.Contents;
const fileNames = files.map(function (file) { return file.Key; });
const fileHtml = fileNames.map(function(fileName) {
return buildFileHtml(fileName)
}).join()
contentContainer().innerHTML = fileHtml
}
});
}
function buildFileHtml(fileName) {
return `<div class="panel panel-default">
<div class="panel-heading">${fileName}</div>
<div class="panel-body">
<h5>Versions</h5>
<div id="${fileName}-versions"></div>
<h5>Text</h5>
<div id="${fileName}-text"></div>
</div>
</div>`
}function addFile() {
const files = document.getElementById('upload').files;
const file = files[0];
const fileName = file.name;
const params = {
Key: fileName,
Body: file,
ACL: 'public-read'
}
s3.putObject(params, function(err, data) {
if (err) {
return alert('There was an error uploading your file: ', err.message);
} else {
listFiles()
}
});
}

Upload both files to your website bucket (granting public read permission to both).

Before you visit your website URL, create a my_file.txt file from the command line with echo "my first line" > my_file.txt. Visit your website URL and upload the file you just created. You should see the file name listed in the browser.

Create a Script to List All Versions of the Files in the Bucket

In this step, you will update your app.js file to list the versions of each file. Update /add the functions below:

//update listFiles function to add a call to a new function: listVersionsForFilesfunction listFiles() {
clearContent()
s3.listObjects({Delimiter: '/'}, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
const files = data.Contents;
const fileNames = files.map(function (file) { return file.Key; });
const fileHtml = fileNames.map(function(fileName) {
return buildFileHtml(fileName)
}).join()
contentContainer().innerHTML = fileHtml
listVersionsForFiles(fileNames)
}
});
}
//ADD the following functions//iterates each file and calls listVersions with that file
function listVersionsForFiles(fileNames) {
fileNames.forEach(function(fileName) { listVersions(fileName) })
}
//uses AWS SDK to get all versions of the file
//calls new function buildVersionHtml and inserts HTML onto DOM
function listVersions(fileName) {
const params = {
Prefix: fileName
}
s3.listObjectVersions(params, function(err, data) {
if (err) {
console.log({err});
} else {
const versions = data.Versions
const versionHtml = buildVersionHtml(fileName, versions)
container = document.getElementById(`${fileName}-versions`)
container.innerHTML = versionHtml
}
});
}
//builds the HTML for the version list
//includes a button that calls a showVersion function
function buildVersionHtml(fileName, versions) {
const versionList = versions.map(function(version) {
const uploadedAgo = moment(version.LastModified).fromNow();
return `<p><button class="btn btn-xs btn-info" data-file="${fileName}" data-id="${version.VersionId}" onClick="showVersion(event)">Show</button>Uploaded: ${uploadedAgo}</p>`
})
return versionList.join('')
}
//to be implemented in the next step
function showVersion(event) {
console.log("to be implemented")
}

Upload the new app.js file to your website bucket (grant public read permission). Navigate to your website URL, refresh the page. You will see your file listed, along with the single version of that file.

Create a Script to Show the Contents of a File Version

In this step you will implement the showVersion method that you added in the last step.

Update your app.js file by changing the showVersion method to the following:

//uses AWS SDK to get the specific version of the object 
//inserts the file contents onto the DOM
function showVersion(event) {
const id = event.target.dataset.id
const fileName = event.target.dataset.file
const params = {
Key: fileName,
VersionId: id
}
s3.getObject(params, function(err, data) {
if (err) {
console.log({err})
} else {
let objectBody = data.Body.toString('utf-8')
let container = document.getElementById(`${fileName}-text`)
container.innerHTML = objectBody
}
})
}

Upload the new app.js file to your website bucket (grant public read permission). Navigate to your website URL, refresh the page. This time, you can click the Show button and you will see the text of your file displayed.

Now, you should add another version of that file to S3. First, from your command line, add additional text to the file: echo "my second line" >> my_file.txt

Then, upload the file via the website. You will see a new version shown in the version list. When you click Show on the new version, the new text should be displayed. When you click Show on the previous version, only the first line should be displayed.

Clean Up

Navigate to your S3 dashboard. Click the bucket symbol next to the file bucket name. Then click Delete Bucket. Type the name of the bucket, and click Confirm. Do the same for the website bucket.

Delete your Cognito identity pool following the instructions shown here.

Next

DynamoDB — Exercise 3.1 Get/Add Items to DynamoDB Tables

--

--

Kerry Sheldon

Software Developer. Graduate of Turing School of Software and Design.