@xompass/pkgcloud
Version:
A provider agnostic cloud library for Node.js
229 lines (191 loc) • 5.87 kB
JavaScript
/*
* files.js: Instance methods for working with files from AWS S3
*
* (C) 2012 Charlie Robbins, Ken Perkins, Ross Kukulinski & the Contributors.
*
*/
const base = require('../../../core/storage');
const pkgcloud = require('../../../../../lib/pkgcloud');
const through = require('through2');
const storage = pkgcloud.providers.amazon.storage;
const _ = require('lodash');
//
// ### function removeFile (container, file, callback)
// #### @container {string} Name of the container to destroy the file in
// #### @file {string} Name of the file to destroy.
// #### @callback {function} Continuation to respond to when complete.
// Destroys the `file` in the specified `container`.
//
exports.removeFile = function (container, file, callback) {
const self = this;
if (container instanceof storage.Container) {
container = container.name;
}
if (file instanceof storage.File) {
file = file.name;
}
self.s3.deleteObject(
{
Bucket: container,
Key: file,
},
function (err, data) {
return err ? callback(err) : callback(null, !!data.DeleteMarker);
},
);
};
exports.upload = function (options) {
const self = this;
// check for deprecated calling with a callback
if (typeof arguments[arguments.length - 1] === 'function') {
self.emit(
'log::warn',
'storage.upload no longer supports calling with a callback',
);
}
const s3Options = {};
if (typeof self.config.defaultUploadParams === 'object') {
Object.assign(s3Options, self.config.defaultUploadParams);
}
s3Options.Bucket =
options.container instanceof base.Container
? options.container.name
: options.container;
s3Options.Key =
options.remote instanceof base.File ? options.remote.name : options.remote;
const s3Settings = {
queueSize: options.queueSize || 1,
partSize: options.partSize || 5 * 1024 * 1024,
};
if (options.contentType) {
s3Options.ContentType = options.contentType;
}
if (options.contentEncoding) {
s3Options.ContentEncoding = options.contentEncoding;
}
// use ACL until a more obvious permission generalization is available
if (options.acl) {
s3Options.ACL = options.acl;
}
// add AWS specific options
if (options.cacheControl || options.CacheControl) {
s3Options.CacheControl = options.cacheControl || options.CacheControl;
}
if (options.ServerSideEncryption) {
s3Options.ServerSideEncryption = options.ServerSideEncryption;
}
// we need a writable stream because aws-sdk listens for an error event on writable
// stream and redirects it to the provided callback - without the writable stream
// the error would be emitted twice on the returned proxyStream
const writableStream = through();
// we need a proxy stream so we can always return a file model
// via the 'success' event
const proxyStream = through();
s3Options.Body = writableStream;
const managedUpload = self.s3.upload(s3Options, s3Settings);
proxyStream.managedUpload = managedUpload;
managedUpload.send(function (err, data) {
if (err) {
return proxyStream.emit('error', err);
}
return proxyStream.emit('success', new storage.File(self, data));
});
proxyStream.pipe(writableStream);
return proxyStream;
};
exports.download = function (options) {
const self = this;
const s3params = {
Bucket:
options.container instanceof base.Container
? options.container.name
: options.container,
Key:
options.remote instanceof base.File
? options.remote.name
: options.remote,
};
if (typeof options.start === 'number' && typeof options.end === 'number') {
s3params.Range = `bytes=${options.start}-${options.end}`;
}
return self.s3.getObject(s3params).createReadStream();
};
exports.getFile = function (container, file, callback) {
const containerName =
container instanceof base.Container ? container.name : container;
const self = this;
self.s3.headObject(
{
Bucket: containerName,
Key: file,
},
function (err, data) {
return err
? callback(err)
: callback(
null,
new storage.File(
self,
_.extend(data, {
container: container,
name: file,
}),
),
);
},
);
};
exports.getFiles = function (container, options, callback) {
const containerName =
container instanceof base.Container ? container.name : container;
const self = this;
if (typeof options === 'function') {
callback = options;
options = {};
} else if (!options) {
options = {};
}
const s3Options = {
Bucket: containerName,
};
if (options.marker) {
s3Options.Marker = options.marker;
}
if (options.prefix) {
s3Options.Prefix = options.prefix;
}
if (options.maxKeys) {
s3Options.MaxKeys = options.maxKeys;
}
self.s3.listObjects(s3Options, function (err, data) {
return err
? callback(err)
: callback(
null,
self._toArray(data.Contents).map(function (file) {
file.container = container;
return new storage.File(self, file);
}),
{
isTruncated: data.IsTruncated,
marker: data.Marker,
nextMarker: data.NextMarker,
},
);
});
};
exports.getSignedUrl = function (options) {
const self = this;
const signedUrlConfig = self.config.signedUrl;
return self.s3.getSignedUrl('getObject', {
Bucket:
options.container instanceof base.Container
? options.container.name
: options.container,
Key:
options.remote instanceof base.File
? options.remote.name
: options.remote,
Expires: options.expiresIn || signedUrlConfig.expiresIn || 3600,
});
};