Skip to content

Commit 3191793

Browse files
committed
Merge pull request #113 from skinp/s3-files
Add support for saving files to AWS S3
2 parents a814655 + 2008c4d commit 3191793

File tree

5 files changed

+91
-6
lines changed

5 files changed

+91
-6
lines changed

FilesAdapter.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Adapter classes must implement the following functions:
66
// * create(config, filename, data)
77
// * get(config, filename)
8+
// * location(config, req, filename)
89
//
910
// Default is GridStoreAdapter, which requires mongo
1011
// and for the API server to be using the ExportAdapter

GridStoreAdapter.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Requires the database adapter to be based on mongoclient
55

66
var GridStore = require('mongodb').GridStore;
7+
var path = require('path');
78

89
// For a given config object, filename, and data, store a file
910
// Returns a promise
@@ -32,7 +33,16 @@ function get(config, filename) {
3233
});
3334
}
3435

36+
// Generates and returns the location of a file stored in GridStore for the
37+
// given request and filename
38+
function location(config, req, filename) {
39+
return (req.protocol + '://' + req.get('host') +
40+
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
41+
'/' + encodeURIComponent(filename));
42+
}
43+
3544
module.exports = {
3645
create: create,
37-
get: get
46+
get: get,
47+
location: location
3848
};

S3Adapter.js

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// S3Adapter
2+
//
3+
// Stores Parse files in AWS S3.
4+
5+
var AWS = require('aws-sdk');
6+
var path = require('path');
7+
8+
var DEFAULT_REGION = "us-east-1";
9+
var DEFAULT_BUCKET = "parse-files";
10+
11+
// Creates an S3 session.
12+
// Providing AWS access and secret keys is mandatory
13+
// Region and bucket will use sane defaults if omitted
14+
function S3Adapter(accessKey, secretKey, options) {
15+
options = options || {};
16+
17+
this.region = options.region || DEFAULT_REGION;
18+
this.bucket = options.bucket || DEFAULT_BUCKET;
19+
this.bucketPrefix = options.bucketPrefix || "";
20+
this.directAccess = options.directAccess || false;
21+
22+
s3Options = {
23+
accessKeyId: accessKey,
24+
secretAccessKey: secretKey,
25+
params: {Bucket: this.bucket}
26+
};
27+
AWS.config.region = this.region;
28+
this.s3 = new AWS.S3(s3Options);
29+
}
30+
31+
// For a given config object, filename, and data, store a file in S3
32+
// Returns a promise containing the S3 object creation response
33+
S3Adapter.prototype.create = function(config, filename, data) {
34+
var params = {
35+
Key: this.bucketPrefix + filename,
36+
Body: data,
37+
};
38+
if (this.directAccess) {
39+
params.ACL = "public-read"
40+
}
41+
42+
return new Promise((resolve, reject) => {
43+
this.s3.upload(params, function(err, data) {
44+
if (err !== null) return reject(err);
45+
resolve(data);
46+
});
47+
});
48+
}
49+
50+
// Search for and return a file if found by filename
51+
// Returns a promise that succeeds with the buffer result from S3
52+
S3Adapter.prototype.get = function(config, filename) {
53+
var params = {Key: this.bucketPrefix + filename};
54+
55+
return new Promise((resolve, reject) => {
56+
this.s3.getObject(params, (err, data) => {
57+
if (err !== null) return reject(err);
58+
resolve(data.Body);
59+
});
60+
});
61+
}
62+
63+
// Generates and returns the location of a file stored in S3 for the given request and
64+
// filename
65+
// The location is the direct S3 link if the option is set, otherwise we serve
66+
// the file through parse-server
67+
S3Adapter.prototype.location = function(config, req, filename) {
68+
if (this.directAccess) {
69+
return ('https://' + this.bucket + '.s3.amazonaws.com' + '/' +
70+
this.bucketPrefix + filename);
71+
}
72+
return (req.protocol + '://' + req.get('host') +
73+
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
74+
'/' + encodeURIComponent(filename));
75+
}
76+
77+
module.exports = S3Adapter;

files.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ var bodyParser = require('body-parser'),
77
middlewares = require('./middlewares.js'),
88
mime = require('mime'),
99
Parse = require('parse/node').Parse,
10-
path = require('path'),
1110
rack = require('hat').rack();
1211

1312
var router = express.Router();
@@ -44,10 +43,7 @@ var processCreate = function(req, res, next) {
4443
FilesAdapter.getAdapter().create(req.config, filename, req.body)
4544
.then(() => {
4645
res.status(201);
47-
var location = (req.protocol + '://' + req.get('host') +
48-
path.dirname(req.originalUrl) + '/' +
49-
req.config.applicationId + '/' +
50-
encodeURIComponent(filename));
46+
var location = FilesAdapter.getAdapter().location(req.config, req, filename);
5147
res.set('Location', location);
5248
res.json({ url: location, name: filename });
5349
}).catch((error) => {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"license": "BSD-3-Clause",
1111
"dependencies": {
12+
"aws-sdk": "~2.2.33",
1213
"bcrypt-nodejs": "0.0.3",
1314
"body-parser": "~1.12.4",
1415
"deepcopy": "^0.5.0",

0 commit comments

Comments
 (0)