Skip to content

Commit eaa315e

Browse files
refactor + adding get functionality
1 parent 1116f04 commit eaa315e

File tree

6 files changed

+98
-63
lines changed

6 files changed

+98
-63
lines changed

README.MD

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ The base use case is the ability to upload remote images into a cloud storage bu
66
(Note: some providers have resizing capabilities inbuilt [eg. GCP], this project will examine other methods to implement the same)
77

88
## API Requirements
9-
- Upload Images via URI
10-
- Download Images
9+
- Upload Images via URL
10+
- Download Images / provide Signed URL
1111
- Resize Images
1212
- 80 day expiry
1313

@@ -23,6 +23,8 @@ The base use case is the ability to upload remote images into a cloud storage bu
2323
2. Add Project_ID to config.json
2424
TODO: add script to deploy.sh to add Project_ID to bucket name
2525

26+
### Service Accounts
27+
2628
## Leads
2729
Consider spinning up app in Firebase
2830
Resumable file uploads: https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
@@ -35,7 +37,8 @@ Pub/Sub: https://github.com/googleapis/nodejs-pubsub/
3537
## TODO
3638
- Filetype verification
3739
- Partitioned file uploads
38-
40+
- Caching
41+
- Match Catch expiry to Bucket item expiration
3942

4043
## Architecture
4144
The API endpoint footprint is small and doesn't need to maintain any state. This makes it a prime candidate for Lamba-style webhooks. (Benefits: Auto-scaling, minimal upkeep, smaller code footprint etc.)

config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
"project_id":"307972859230",
33
"bucket_name":"image_api_307972859230",
44
"keys": {
5+
"path":"./keys",
56
"cloud_storage": {
67
"path":"./keys/cloudStorage.json"
8+
},
9+
"cloud_functions": {
10+
"path":"./keys/functions.json"
711
}
812
}
913
}

experiments/gcp/download.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const {Storage} = require('@google-cloud/storage');
4+
5+
const config = require('./../../config.json');
6+
const storage = new Storage({
7+
projectId: config.project_id,
8+
keyFilename: './keys/cloudStorage.json'
9+
});
10+
11+
function downloadByID(fileName, bucket) {
12+
let file = bucket.file(fileName);
13+
return new Promise((resolve, reject) => {
14+
file.createReadStream()
15+
.on('response', (response) => { response.pause(); resolve(response); })
16+
.on('error', (reject))
17+
.pipe(); // localCache
18+
})
19+
.catch((error) => { return { error }; }); // TODO: Add more detailed error handling. - statusCode etc.
20+
}
21+
22+
exports.download = function (id) {
23+
return downloadByID(id, storage.bucket(config.bucket_name));
24+
};

experiments/gcp/get.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const {Storage} = require('@google-cloud/storage');
4+
5+
const config = require('./../../config.json');
6+
const storage = new Storage({
7+
projectId: config.project_id,
8+
keyFilename: './keys/cloudStorage.json'
9+
});
10+
11+
12+
/**
13+
* Returns a Signed URL to access the uploaded file
14+
* TODO: set expiry date to match the bucket's expiry rules for files
15+
*/
16+
function getURL(fileName, bucket) {
17+
let file = bucket.file(fileName);
18+
return new Promise((resolve, reject) => {
19+
file.getSignedUrl({action:'read', expires:'01-01-2020'}, function(err, url) {
20+
if (err) reject(err);
21+
else resolve(url);
22+
});
23+
});
24+
}
25+
26+
exports.get = function (fileName) {
27+
return getURL(fileName, storage.bucket(config.bucket_name));
28+
};

experiments/gcp/upload.js

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
'use strict';
22

33
const request = require('request');
4+
const path = require('path');
5+
const {Storage} = require('@google-cloud/storage');
46

57
const config = require('./../../config.json');
6-
7-
const {Storage} = require('@google-cloud/storage');
88
const storage = new Storage({
99
projectId: config.project_id,
1010
keyFilename: './keys/cloudStorage.json'
@@ -23,43 +23,6 @@ function uploadByURL(url, bucket, fileName) {
2323
}
2424
// TODO: file.createResumableUpload
2525

26-
exports.upload = function (url, fileName) {
27-
return uploadByURL(url, storage.bucket(config.bucket_name), fileName);
28-
};
29-
30-
function downloadByID(fileName, bucket) {
31-
let file = bucket.file(fileName);
32-
return new Promise((resolve, reject) => {
33-
file.createReadStream()
34-
.on('response', (response) => { response.pause(); resolve(response); })
35-
.on('error', (reject))
36-
.pipe(); // localCache
37-
})
38-
.catch((error) => { return { error }; }); // TODO: Add more detailed error handling. - statusCode etc.
39-
}
40-
41-
exports.download = function (id) {
42-
return downloadByID(id, storage.bucket(config.bucket_name));
43-
};
44-
45-
46-
/**
47-
* Returns a Signed URL to access the uploaded file
48-
* TODO: set expiry date to match the bucket's expiry rules for files
49-
*/
50-
function getURL(fileName, bucket) {
51-
let file = bucket.file(fileName);
52-
file.getSignedUrl({action:'read', expires:'01-01-2020'}, function(err, url) {
53-
if (err) {
54-
console.error(err);
55-
return;
56-
}
57-
return url;
58-
});
59-
}
60-
61-
62-
63-
exports.get = function (fileName) {
64-
return getURL(fileName, storage.bucket(config.bucket_name));
26+
exports.upload = function (url) {
27+
return uploadByURL(url, storage.bucket(config.bucket_name), path.basename(url));
6528
};

index.js

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const url = require('url');
33
//const uuid = require('uuidv4');
44
const request = require('request');
5+
const path = require('path');
56
//const rp = require('request-promise');
67

78
const config = require('./config.json');
@@ -46,52 +47,64 @@ function uploadByURL(url, bucket, fileName) {
4647
.catch((error) => { return { error }; }); // TODO: Add more detailed error handling. - statusCode etc.
4748
}
4849

49-
function upload(url, fileName) {
50-
return uploadByURL(url, storage.bucket(config.bucket_name), fileName);
50+
function upload(url) {
51+
return uploadByURL(url, storage.bucket(config.bucket_name), path.basename(url));
5152
}
5253

54+
5355
/**
54-
*
55-
* TODO: refactor to adapt based on content-type
56+
* Returns a Promise which resolves to a Signed URL to access the uploaded file
57+
* TODO: set expiry date to match the bucket's expiry rules for files
5658
*/
57-
function getImage(req, res) {
58-
59-
res.status(200).send();
59+
function getURL(fileName, bucket) {
60+
let file = bucket.file(fileName);
61+
return new Promise((resolve, reject) => {
62+
file.getSignedUrl({action:'read', expires:'01-01-2020'}, function(err, url) {
63+
if (err) reject(err);
64+
else resolve(url);
65+
});
66+
});
6067
}
6168

62-
/*
63-
switch (req.get('content-type')) {
69+
function getImage(fileName, contentType) {
70+
switch (contentType) {
6471
case 'application/JSON':
65-
break;
66-
case 'image/jpeg':
72+
return getURL(fileName, storage.bucket(config.bucket_name));
73+
/*case 'image/jpeg':
6774
break;
6875
case 'image/png':
69-
break;
76+
break;*/
7077
}
71-
*/
78+
}
7279

73-
// Lambdas
7480
/**
7581
* images - /images webhook.
7682
*/
7783
exports.images = (req, res) => {
78-
if(!res.body.urls) res.status(400).json({error:'noURLs', message:'No URLs were provided.'});
79-
let urls = res.body.urls;
84+
if(req.method == 'PUT' && !req.body.urls) res.status(400).json({error:'noURLs', message:'No URLs were provided.'});
85+
if(req.method == 'GET' && !req.body.filename) res.status(400).json({error:'noImageName', message:'No Image Name was provided'});
86+
let urls = req.body.urls;
87+
let fileName = req.body.filename;
88+
8089
switch(req.method) {
81-
case 'POST':
90+
case 'PUT':
8291
// For each URL, verify, upload it, then aggregate the results to return as JSON object.
8392
Promise.all(
8493
urls.map((url) => {
8594
return verifyURL(url)
86-
.then((url) => { upload(url, url); }); // naming convention will likely need to change for multi-users
95+
// TODO: Check pre-existence of file
96+
.then((url) => { upload(url); });
8797
})
8898
)
8999
// TODO: Change status IDs depending on error/s from Promises.
100+
// Promise failure here is rudimentary - need to handle mixed results.
90101
.then(res.status(202).json)
91102
.catch(res.status(400).json);
92103
break;
93104
case 'GET':
94-
getImage(req, res);
105+
getImage(fileName, req.get('content-type'))
106+
.then(res.status(202).json)
107+
.catch(res.status(400).json);
95108
break;
96109
}
97110
};

0 commit comments

Comments
 (0)