Skip to content

Commit

Permalink
Merge pull request #113 from skinp/s3-files
Browse files Browse the repository at this point in the history
Add support for saving files to AWS S3
  • Loading branch information
gfosco committed Feb 1, 2016
2 parents a814655 + 2008c4d commit 3191793
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 6 deletions.
1 change: 1 addition & 0 deletions FilesAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Adapter classes must implement the following functions:
// * create(config, filename, data)
// * get(config, filename)
// * location(config, req, filename)
//
// Default is GridStoreAdapter, which requires mongo
// and for the API server to be using the ExportAdapter
Expand Down
12 changes: 11 additions & 1 deletion GridStoreAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Requires the database adapter to be based on mongoclient

var GridStore = require('mongodb').GridStore;
var path = require('path');

// For a given config object, filename, and data, store a file
// Returns a promise
Expand Down Expand Up @@ -32,7 +33,16 @@ function get(config, filename) {
});
}

// Generates and returns the location of a file stored in GridStore for the
// given request and filename
function location(config, req, filename) {
return (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
'/' + encodeURIComponent(filename));
}

module.exports = {
create: create,
get: get
get: get,
location: location
};
77 changes: 77 additions & 0 deletions S3Adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// S3Adapter
//
// Stores Parse files in AWS S3.

var AWS = require('aws-sdk');
var path = require('path');

var DEFAULT_REGION = "us-east-1";
var DEFAULT_BUCKET = "parse-files";

// Creates an S3 session.
// Providing AWS access and secret keys is mandatory
// Region and bucket will use sane defaults if omitted
function S3Adapter(accessKey, secretKey, options) {
options = options || {};

this.region = options.region || DEFAULT_REGION;
this.bucket = options.bucket || DEFAULT_BUCKET;
this.bucketPrefix = options.bucketPrefix || "";
this.directAccess = options.directAccess || false;

s3Options = {
accessKeyId: accessKey,
secretAccessKey: secretKey,
params: {Bucket: this.bucket}
};
AWS.config.region = this.region;
this.s3 = new AWS.S3(s3Options);
}

// For a given config object, filename, and data, store a file in S3
// Returns a promise containing the S3 object creation response
S3Adapter.prototype.create = function(config, filename, data) {
var params = {
Key: this.bucketPrefix + filename,
Body: data,
};
if (this.directAccess) {
params.ACL = "public-read"
}

return new Promise((resolve, reject) => {
this.s3.upload(params, function(err, data) {
if (err !== null) return reject(err);
resolve(data);
});
});
}

// Search for and return a file if found by filename
// Returns a promise that succeeds with the buffer result from S3
S3Adapter.prototype.get = function(config, filename) {
var params = {Key: this.bucketPrefix + filename};

return new Promise((resolve, reject) => {
this.s3.getObject(params, (err, data) => {
if (err !== null) return reject(err);
resolve(data.Body);
});
});
}

// Generates and returns the location of a file stored in S3 for the given request and
// filename
// The location is the direct S3 link if the option is set, otherwise we serve
// the file through parse-server
S3Adapter.prototype.location = function(config, req, filename) {
if (this.directAccess) {
return ('https://' + this.bucket + '.s3.amazonaws.com' + '/' +
this.bucketPrefix + filename);
}
return (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
'/' + encodeURIComponent(filename));
}

module.exports = S3Adapter;
6 changes: 1 addition & 5 deletions files.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ var bodyParser = require('body-parser'),
middlewares = require('./middlewares.js'),
mime = require('mime'),
Parse = require('parse/node').Parse,
path = require('path'),
rack = require('hat').rack();

var router = express.Router();
Expand Down Expand Up @@ -44,10 +43,7 @@ var processCreate = function(req, res, next) {
FilesAdapter.getAdapter().create(req.config, filename, req.body)
.then(() => {
res.status(201);
var location = (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' +
req.config.applicationId + '/' +
encodeURIComponent(filename));
var location = FilesAdapter.getAdapter().location(req.config, req, filename);
res.set('Location', location);
res.json({ url: location, name: filename });
}).catch((error) => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"license": "BSD-3-Clause",
"dependencies": {
"aws-sdk": "~2.2.33",
"bcrypt-nodejs": "0.0.3",
"body-parser": "~1.12.4",
"deepcopy": "^0.5.0",
Expand Down

0 comments on commit 3191793

Please sign in to comment.