Created
March 8, 2015 04:03
-
-
Save stevecass/ca398401437063ae8d3f to your computer and use it in GitHub Desktop.
Meteor and S3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Prerequisite: meteor add peerlibrary:aws-sdk | |
Terminology: | |
AWS: Amazon web services | |
S3: Simple storage service (one of the above.) | |
Create a bucket on S3. Use AWS Identity and Access Manager to create | |
a user that your app can log in as . Attach a policy to that user | |
giving it the appropriate level of access to your bucket. | |
Once the meteor package is installed, aws-sdk will make a global | |
AWS variable available to you. You need to configure it with | |
key and secret generated for your app user (don't put these in your | |
repo. Either use Meteor.settings or environment variables.) | |
You can then define a meteor method (sample below) to take file | |
content and use the aws sdk to upload it to s3 into a bucket and | |
filename of your choosing. | |
To use folders inside the bucket, use file names like | |
"folder1/folder2/myfile.jpg". | |
S3 will auto-create the folder for you if it's not already there. | |
*/ | |
// get AWS access data | |
AWS.config.update({ | |
accessKeyId: proc.env.AWS_KEY, | |
secretAccessKey: proc.env.AWS_SECRET | |
}); | |
//The main upload function | |
Meteor.methods({ | |
"uploadToS3":function(fileName, bucket, fileData){ | |
s3 = new AWS.S3({endpoint:"s3.amazonaws.com"}); | |
s3.putObject( { | |
Bucket: bucket, | |
ACL:'public-read', | |
Key: fileName, | |
ContentType: "image/jpeg", | |
Body:fileData | |
}, | |
function(err, data) { | |
if(err){ | |
console.log('upload error:',err); | |
}else{ | |
console.log('upload was succesful',data); | |
/* | |
At this point you know the upload succeeded | |
and "data" contains an ETag | |
The file is public-accessible at | |
https://s3.amazonaws.com/bucket/fileName | |
where bucket and fileName are the values passed to this | |
function. | |
If securty is a concern, you may need to take | |
a slightly different approach, such as storing the file | |
privately, and using additional calls to create | |
temporary urls for the file as and when you need | |
to display it in your app. | |
For just preventing slurping you could probably | |
get away with randomized file names. S3 won't allow | |
your folders to be browsed even if the files are public. | |
*/ | |
} | |
} | |
); | |
} | |
}); | |
/* Creating "bodyData" to upload from a client-side file input */ | |
Template.uploadForm.events({ | |
'submit' : function(event){ | |
event.preventDefault(); | |
var inp = document.getElementById('file-select'); | |
var fileName = inp.value; | |
// Massage filename. Remove C:\fakedata nonsense. | |
// Remove spaces and add a timestamp | |
// Change this for your requirements obv. | |
// Remember not to trust user input. | |
fileName = new Date().getTime() + "_" + fileName.replace(/^.*\\/, ""); | |
fileName = fileName.replace(/[^0-9a-z.]/ig, "_"); | |
fileName = fileName.replace(/__+/g, "_"); | |
var file = inp.files[0]; | |
if (file) { | |
var reader = new FileReader(); | |
reader.readAsBinaryString(file); | |
/* reader will fire the onloadend once it's read | |
all of the file. So you can call the | |
upload function in a handler for that event. */ | |
reader.onloadend = function(){ | |
Meteor.call("uploadToS3", fileName, reader.result); | |
}; | |
} | |
} | |
}); | |
/* Creating "bodyData" from a device camera with the mdg:camera package */ | |
/* The MeteorCamera.getPicture([options], callback) function | |
that mdg:camera provides calls your callback with a reference to | |
the photo data as a base 64 format data uri. | |
You need to read that data and convert it into something you | |
can upload to s3 as a file. | |
*/ | |
function dataURItoBinary(dataURI) { | |
var binary = atob(dataURI.split(',')[1]); | |
var array = []; | |
for(var i = 0; i < binary.length; i++) { | |
array.push(binary.charCodeAt(i)); | |
} | |
return new Uint8Array(array); | |
} | |
Template.takePhoto.events({ | |
'click .capture': function(){ | |
MeteorCamera.getPicture({}, function(error, data){ | |
var photoData = dataURItoBinary(data); | |
// Think about how you'll name your files.... | |
var fileName = 'app_' + new Date().getTime() + ".jpg"; | |
Meteor.call("uploadToS3", fileName, photoData); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've inadvertently hard-coded image/jpeg on line 42. you may prefer to parameterize that (if you're not dealing just with images.)