glimmer
Version:
The most powerful file upload toolkit you've yet to see.
165 lines (130 loc) • 3.54 kB
JavaScript
/**
* Dependencies
*/
var path = require('path')
, Stream = require('stream')
, Busboy = require('busboy')
, Promise = require('bluebird')
, errbot = require('errbot')
, async = require('async')
, includes = require('lodash.includes')
, extend = require('ampersand-class-extend');
/**
* Base Class
*/
class Glimmer {
static register (key, uploader) {
this.uploaders[key] = uploader;
}
static get (key) {
return this.uploaders[key]
}
static parse (context, options, done) {
options = options || {};
context = context.req || context;
var self = this
, meta = options.meta || {}
, parser = new Busboy({ headers: context.headers })
, deferred;
delete options.meta;
context.pipe(parser);
deferred = new Promise(function (resolve, reject) {
var count = 0
, files = {}
, onEnd;
onEnd = function () {
if (0 !== count) return;
return resolve(files);
};
parser.on('error', function (err) {
return reject(err);
});
parser.on('file', function (fieldname, stream, filename, enc, mime) {
let mapping = options[fieldname]
, Uploader = self.uploaders[mapping];
if (!filename || !mapping || !Uploader) return stream.resume();
count++;
new Uploader(stream, filename, meta).save().then(function (stored) {
count--;
files[fieldname] = stored;
onEnd();
}, function (err) {
return reject(err);
});
});
parser.on('finish', function () {
onEnd();
});
});
return deferred.nodeify(done);
}
static store (stream, mapping, meta, done) {
if (!stream || !stream.readable) {
throw new Error('You must provide an input stream.');
}
if ('function' === typeof meta) {
done = meta;
meta = null;
}
var Uploader = this.uploaders[mapping]
, filename = path.basename(stream.path);
if (!Uploader) throw new Error('You must provide a valid key.');
return new Uploader(stream, filename, meta).save().nodeify(done);
}
}
Glimmer.uploaders = {};
/**
* Uploader
*/
class Uploader {
constructor (source, filename, meta) {
if (!source) throw new Error('Please pass a source stream.');
this.source = source;
this.input = new Stream.PassThrough();
this.filename = filename;
this.meta = meta || {};
this.transformers = {};
this.extensions = [];
this.source.pipe(this.input);
this.configure();
}
configure () {}
accepts (extensions) {
if (Array.isArray(extensions)) {
this.extensions = extensions;
} else {
this.extensions = [extensions];
}
}
validate () {
if (!this.extensions.length) return true;
return includes(
this.extensions,
path.extname(this.filename).slice(1)
);
}
transform (method) {
this.transformers[method] = this[method].bind(this);
}
save (done) {
var self = this;
return new Promise(function (resolve, reject) {
if (!self.validate()) {
return reject(errbot.badRequest('Please try another file type.'));
}
async.parallel(self.transformers, function (err, saved) {
if (err) return reject(err);
resolve(saved);
});
}).nodeify(done);
}
}
/**
* Class Methods
*/
Uploader.extend = extend;
/**
* Expose Glimmer
*/
exports = module.exports = Glimmer;
exports.Uploader = Uploader;