It is hard to setup a demo for AWS S3 upload. First it will trash my bucket and I have to support Web server which gives you policies. So I created small demo video.
I did not find anything I really like. What I needed from such a module is.
- Lightweight.
- Independence of any other 3d party libraries.
- Complete control over how it looks and works. In one case I just need a button to upload avatar, or sometimes a series of videos.
- Control over error handling.
Install with bower.
bower install angular-aws-s3-upload --save
Or download. All you need is only angular dependency
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
<script type="text/javascript" src="bower_components/angular-aws-s3-upload/angular-s3.js"></script>
If you want to use ngMessages
with this element you have to add this to your project.
And add dependency to your application.
var app = angular.module('myapp', ['angular.aws.s3'])
That is it. You are good to start create your interface.
This module created such a away that you fully control how your uploader looks. It is rather small framework with AWS s3 tools than ready to use component.
Ready to use components are nice until you what to change something. Directive templates become a real pain for me. yes I can always change template and template path, but I could not find a directive where I can do that and still safely update module without loosing changes.
This is not the case in Angular AWS S3 Uploader. It not only have no template file, it does not have even template parameter. It means that HTML elements, their classes and positions are fully up to you. This directive is a helper and you are the UI master.
The main component of this uploader is a button.
<button class="btn btn-default" s3 options="options" name="uploads" ng-model="form.files" required>
Select File
</button>
You see, you can use button or link or anything else. You set label and classes.
- First is
s3
directive with makes all the magic. - Then
name
attribute if you want usengMessages
. required
attribute is also for using withngMessages
.- You all know
ng-model
right? - And
options
are described later.
Now you have to add interface as you like. If you use multiple upload you can list form.files
with ng-repeat
in a table below, or just show confirmation alert. Thanks to Angular 2 way binding, what ever is added to form.files
will be available outside directive.
One important thing to know is upload
button. If your component is not for immediate upload, then you can place another button.
<button class="pull-right btn btn-primary" s3-upload="form.files" ng-disabled="start_upload_state">
Start Upload
</button>
all you have to do is to add s3-upload="form.files"
directive and when you click this button all attached files will start upload. This button will be also automatically hidden or visible, enabled or disabled depending on conditions.
Uploader gives you very wide variety of options to set. Options is an object. So you may insert it directly.
options="{limit:1}"
or use scope
options="options"
$scope.options = {
limit: 1,
immediate: true
}
Here is the list of all options.
Options | Type | Description |
---|---|---|
multiple | boolean | Allow multiple select files in file dialog. |
extensions | array | Allowed extensions ['png', 'jpg', 'mp4'] . |
immediate | boolean | Start upload immediately or when user click upload button. |
bucket | string | Name of S3 bucket. |
acl | string | What acl rules apply to file after upload. Eg: 'public-read'. |
folder | string | Directory in the bucket where file have to be saved. Should end with a trailing slash / . |
filename | string | Override file name. This option is useful only if you have single file upload. For example user upload avatar and you upload it to /avatars/[USR_ID]/avatar.png . So it is always the same name and this way you always know path to user avatar. It also helps to avoid file management. If user change avatar you do not eed to think to remove old one. Because it will simply override. |
filesize | num | Number in bytes maximum allowed size per user. |
totalsize | num | Number in bytes maximum allowed size for list of files. |
region | string | AWS S3 region: 'us-east-1'. |
limit | num | Number of files to uplaod. 0 is unlimited. |
on_success | function | callback on success file upload. |
on_upload_state_change | function | Callback passed true when uploading starts and false when it stops. |
replace | boolean | Only works with limit set to 1. In this case on new selection it will simply override existing file. |
policy | object | AWS policy object. Explained later. |
policyUrl | string | URL to get policy object. |
We need either policy object or url which will provide this object. The object have to contain 3 keys.
key
- The Access Key Identifier credential for your Amazon Web Service account.policy
- A Base64-encoded policy document that applies rules to file uploads sent by the S3 POST form. This document is used to authorize the form, and to impose conditions on the files that can be uploaded. Policy documents will be described in more detail below.signature
- A signature value that authorizes the form and proves that only you could have created it. This value is calculated by signing the Base64-encoded policy document with your AWS Secret Key, a process that I will demonstrate below.
Here is the example how it might looks
{
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNi0wNS0xOVQwNTozMDow...l1dfQ==",
"signature":"b0Dd58rD+uDtf2wkALm7+Y2JJG4=",
"key":"AKIAIBUOK6TJJUEDWPBA"
}
Here is the article you could read on how to generate one.
https://aws.amazon.com/articles/1434
I also include policy.php
file in the project as example how to generate a policy.
Another important thing is to configure your S3 bucket permissions. At a
minimum the s3:PutObject
is required. If you use the acl
option,
s3:PutObjectAcl
is also required. Example policy:
{
"Version": "2008-10-17",
"Id": "PolicyForAngularAwsS3Upload",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[id]:user/[username]"
},
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::[bucket]/*"
}
]
}
Secondly, you have to enable CORS on your bucket. Here is how you can do that.
http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
Long story sort, you have to add to your CORS rules
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>http://www.example1.com</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
</CORSRule>
</CORSConfiguration>
You also have to allow your s3 user to upload documents to s3.
There are 9 errors types.
Error | Description |
---|---|
totalsize |
When total size of all files exceed parameter in options |
totalfiles |
When total number of files is more then set in options. |
required |
When you have required attribute and none of the files uploaded |
extension |
When one of the files is wrong extension then extensions allowed in options. |
filesize |
When one of the files exceed size per file option |
policy_content |
When we get policy from url but it is not an object |
policy_get |
Error accessing policy URL |
policy_set |
There is no policy parameter at all |
upload |
Some error during upload. Usually S3 access error will sendup here |
Now as you know the names, you can handle errors 3 different ways.
Of course you have to include ngMessages
module to your file and add dependency. ngMessages is not included in AngularJS and shipped separately.
First you have to add name
attribute to the same element where you added s3
directive. Let's assume you have form name testForm
and uploader name uploads
, then you might end up with something like this.
<div ng-messages="testForm.uploads.$error">
<div ng-message="totalsize" class="alert-danger alert">Total size of files is too big.</div>
<div ng-message="totalfiles" class="alert-danger alert">Total number of files is more than allowed, please delete some of them.</div>
<div ng-message="required" class="alert-danger alert">At least one file have to be uploaded.</div>
<div ng-message="extension" class="alert-danger alert">At least one file have to be uploaded.</div>
<div ng-message="filesize" class="alert-danger alert">One of the files exceed allowed size.</div>
<div ng-message="policy_content" class="alert-danger alert">URL provides not a policy.</div>
<div ng-message="policy_get" class="alert-danger alert">Cannot get policy over URL provided.</div>
<div ng-message="policy_set" class="alert-danger alert">No policy parameter found.</div>
<div ng-message="upload" class="alert-danger alert">Something happened during upload.</div>
</div>
You may set callbacks in options
. The name of the callback is on_
and then name of the error.
$scope.options = {
on_totalsize: function(files) {}
}
There is one general callback for all errors on_error
if you what to have one method for all errors.
S3 uploader will $emmit
error events. The event name is s3uploader:error:
and then name. So in your code.
$scope.$on('s3uploader:error:filesize', function(e, file){})
There is also one event for all errors if you what to handle all errors in one event s3uploader:error
.