Files
kami-parse-server/src/Adapters/Files/GCSAdapter.js
2016-03-07 00:34:41 -08:00

126 lines
4.2 KiB
JavaScript

// GCSAdapter
// Store Parse Files in Google Cloud Storage: https://cloud.google.com/storage
import { storage } from 'gcloud';
import { FilesAdapter } from './FilesAdapter';
import requiredParameter from '../../requiredParameter';
function requiredOrFromEnvironment(env, name) {
let environmentVariable = process.env[env];
if (!environmentVariable) {
requiredParameter(`GCSAdapter requires an ${name}`);
}
return environmentVariable;
}
function fromEnvironmentOrDefault(env, defaultValue) {
let environmentVariable = process.env[env];
if (environmentVariable) {
return environmentVariable;
}
return defaultValue;
}
export class GCSAdapter extends FilesAdapter {
// GCS Project ID and the name of a corresponding Keyfile are required.
// Unlike the S3 adapter, you must create a new Cloud Storage bucket, as this is not created automatically.
// See https://googlecloudplatform.github.io/gcloud-node/#/docs/master/guides/authentication
// for more details.
constructor(
projectId = requiredOrFromEnvironment('GCP_PROJECT_ID', 'projectId'),
keyFilename = requiredOrFromEnvironment('GCP_KEYFILE_PATH', 'keyfile path'),
bucket = requiredOrFromEnvironment('GCS_BUCKET_NAME', 'bucket name'),
{ bucketPrefix = fromEnvironmentOrDefault('GCS_BUCKET_PREFIX', ''),
directAccess = fromEnvironmentOrDefault('GCS_DIRECT_ACCESS', false) } = {}) {
super();
this._bucket = bucket;
this._bucketPrefix = bucketPrefix;
this._directAccess = directAccess;
let options = {
projectId: projectId,
keyFilename: keyFilename
};
this._gcsClient = new storage(options);
}
// For a given config object, filename, and data, store a file in GCS.
// Resolves the promise or fails with an error.
createFile(config, filename, data, contentType) {
let params = {
contentType: contentType || 'application/octet-stream'
};
return new Promise((resolve, reject) => {
let file = this._gcsClient.bucket(this._bucket).file(this._bucketPrefix + filename);
// gcloud supports upload(file) not upload(bytes), so we need to stream.
var uploadStream = file.createWriteStream(params);
uploadStream.on('error', (err) => {
return reject(err);
}).on('finish', () => {
// Second call to set public read ACL after object is uploaded.
if (this._directAccess) {
file.makePublic((err, res) => {
if (err !== null) {
return reject(err);
}
resolve();
});
} else {
resolve();
}
});
uploadStream.write(data);
uploadStream.end();
});
}
// Deletes a file with the given file name.
// Returns a promise that succeeds with the delete response, or fails with an error.
deleteFile(config, filename) {
return new Promise((resolve, reject) => {
let file = this._gcsClient.bucket(this._bucket).file(this._bucketPrefix + filename);
file.delete((err, res) => {
if(err !== null) {
return reject(err);
}
resolve(res);
});
});
}
// Search for and return a file if found by filename.
// Returns a promise that succeeds with the buffer result from GCS, or fails with an error.
getFileData(config, filename) {
return new Promise((resolve, reject) => {
let file = this._gcsClient.bucket(this._bucket).file(this._bucketPrefix + filename);
// Check for existence, since gcloud-node seemed to be caching the result
file.exists((err, exists) => {
if (exists) {
file.download((err, data) => {
if (err !== null) {
return reject(err);
}
return resolve(data);
});
} else {
reject(err);
}
});
});
}
// Generates and returns the location of a file stored in GCS for the given request and filename.
// The location is the direct GCS link if the option is set,
// otherwise we serve the file through parse-server.
getFileLocation(config, filename) {
if (this._directAccess) {
return `https://${this._bucket}.storage.googleapis.com/${this._bucketPrefix + filename}`;
}
return (config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename));
}
}
export default GCSAdapter;