Skip to content

Instantly share code, notes, and snippets.

@stevecass
Created March 8, 2015 04:03
Show Gist options
  • Save stevecass/ca398401437063ae8d3f to your computer and use it in GitHub Desktop.
Save stevecass/ca398401437063ae8d3f to your computer and use it in GitHub Desktop.
Meteor and S3
/*
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);
}
});
@stevecass
Copy link
Author

I've inadvertently hard-coded image/jpeg on line 42. you may prefer to parameterize that (if you're not dealing just with images.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment