· 8 years ago · Nov 16, 2017, 02:52 PM
1var crypto = require('crypto');
2
3// This is the entry function that produces data for the frontend
4// config is hash of S3 configuration:
5// * bucket
6// * region
7// * accessKey
8// * secretKey
9function s3Credentials(config, params) {
10 return {
11 endpoint_url: "https://" + config.bucket + ".s3.amazonaws.com",
12 params: s3Params(config, params)
13 }
14}
15
16// Returns the parameters that must be passed to the API call
17function s3Params(config, params) {
18 var credential = amzCredential(config);
19 var policy = s3UploadPolicy(config, params, credential);
20 var policyBase64 = new Buffer(JSON.stringify(policy)).toString('base64');
21 return {
22 key: params.filename,
23 acl: 'public-read',
24 success_action_status: '201',
25 policy: policyBase64,
26 "content-type": params.contentType,
27 'x-amz-algorithm': 'AWS4-HMAC-SHA256',
28 'x-amz-credential': credential,
29 'x-amz-date': dateString() + 'T000000Z',
30 'x-amz-signature': s3UploadSignature(config, policyBase64, credential)
31 }
32}
33
34function dateString() {
35 var date = new Date().toISOString();
36 return date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2);
37}
38
39function amzCredential(config) {
40 return [config.accessKey, dateString(), config.region, 's3/aws4_request'].join('/')
41}
42
43// Constructs the policy
44function s3UploadPolicy(config, params, credential) {
45 return {
46 // 5 minutes into the future
47 expiration: new Date((new Date).getTime() + (5 * 60 * 1000)).toISOString(),
48 conditions: [
49 { bucket: config.bucket },
50 { key: params.filename },
51 { acl: 'public-read' },
52 { success_action_status: "201" },
53 // Optionally control content type and file size
54 // A content-type clause is required (even if it's all-permissive)
55 // so that the uploader can specify a content-type for the file
56 ['starts-with', '$Content-Type', ''],
57 ['content-length-range', 0, 100000],
58 { 'x-amz-algorithm': 'AWS4-HMAC-SHA256' },
59 { 'x-amz-credential': credential },
60 { 'x-amz-date': dateString() + 'T000000Z' }
61 ],
62 }
63}
64
65function hmac(key, string) {
66 var hmac = require('crypto').createHmac('sha256', key);
67 hmac.end(string);
68 return hmac.read();
69}
70
71// Signs the policy with the credential
72function s3UploadSignature(config, policyBase64, credential) {
73 var dateKey = hmac('AWS4' + config.secretKey, dateString());
74 var dateRegionKey = hmac(dateKey, config.region);
75 var dateRegionServiceKey = hmac(dateRegionKey, 's3');
76 var signingKey = hmac(dateRegionServiceKey, 'aws4_request');
77 return hmac(signingKey, policyBase64).toString('hex');
78}
79
80module.exports = {
81 s3Credentials: s3Credentials
82}
83
84// Requires jQuery and blueimp's jQuery.fileUpload
85var bucket = 'my-trybucket';
86// client-side validation by fileUpload should match the policy
87// restrictions so that the checks fail early
88var acceptFileType = /.*/i;
89var maxFileSizee = 10000;
90// The URL to your endpoint that maps to s3Credentials function
91var credentialsUrl = '/s3_credentials';
92// The URL to your endpoint to register the uploaded file
93var uploadUrl = '/upload';
94
95window.initS3FileUpload = function($fileInput) {
96 console.log($fileInput);
97 $fileInput.fileupload({
98 // acceptFileTypes: acceptFileType,
99 maxFileSize: maxFileSizee,
100 url: 'https://' + bucket + '.s3.amazonaws.com',
101 paramName: 'file',
102 add: s3add,
103 dataType: 'xml',
104 done: onS3Done
105 });
106};
107
108// This function retrieves s3 parameters from our server API and appends them
109// to the upload form.
110function s3add(e, data) {
111 var filename = data.files[0].name;
112 var contentType = data.files[0].type;
113 console.log("File Name is:"+filename);
114 console.log("File Name is:"+contentType);
115 var params = [];
116 $.ajax({
117 url: credentialsUrl,
118 type: 'GET',
119 dataType: 'json',
120 data: {
121 filename: filename,
122 content_type: contentType
123 },
124 success: function(s3Data) {
125 data.url = s3Data.endpoint_url;
126 data.formData = s3Data.params;
127 data.submit();
128 }
129 });
130 return params;
131};
132
133function onS3Done(e, data) {
134 var s3Url = $(data.jqXHR.responseXML).find('Location').text();
135 var s3Key = $(data.jqXHR.responseXML).find('Key').text();
136 // Typically, after uploading a file to S3, you want to register that file with
137 // your backend. Remember that we did not persist anything before the upload.
138 console.log($('<a/>').attr('href', s3Url).text('File uploaded at '+s3Url).appendTo($('body')));
139};