Add range support

This commit is contained in:
Raymond Feng 2015-11-23 17:16:48 -08:00
parent a204980c82
commit fdb4c0464e
3 changed files with 92 additions and 29 deletions

View File

@ -192,6 +192,11 @@ FileSystemProvider.prototype.download = function (options, cb) {
var fileOpts = {flags: 'r', var fileOpts = {flags: 'r',
autoClose: true }; autoClose: true };
if (options.start) {
fileOpts.start = options.start
fileOpts.end = options.end
}
try { try {
return fs.createReadStream(filePath, fileOpts); return fs.createReadStream(filePath, fileOpts);
} catch (e) { } catch (e) {

View File

@ -14,7 +14,7 @@ var defaultOptions = {
* @callback {Function} cb Callback function * @callback {Function} cb Callback function
* @header storageService.upload(provider, req, res, options, cb) * @header storageService.upload(provider, req, res, options, cb)
*/ */
exports.upload = function (provider, req, res, options, cb) { exports.upload = function(provider, req, res, options, cb) {
if (!cb && 'function' === typeof options) { if (!cb && 'function' === typeof options) {
cb = options; cb = options;
options = {}; options = {};
@ -27,14 +27,14 @@ exports.upload = function (provider, req, res, options, cb) {
var form = new IncomingForm(options); var form = new IncomingForm(options);
var container = options.container || req.params.container; var container = options.container || req.params.container;
var fields = {}, files = {}; var fields = {}, files = {};
form.handlePart = function (part) { form.handlePart = function(part) {
var self = this; var self = this;
if (part.filename === undefined) { if (part.filename === undefined) {
var value = '' var value = ''
, decoder = new StringDecoder(this.encoding); , decoder = new StringDecoder(this.encoding);
part.on('data', function (buffer) { part.on('data', function(buffer) {
self._fieldsSize += buffer.length; self._fieldsSize += buffer.length;
if (self._fieldsSize > self.maxFieldsSize) { if (self._fieldsSize > self.maxFieldsSize) {
self._error(new Error('maxFieldsSize exceeded, received ' + self._fieldsSize + ' bytes of field data')); self._error(new Error('maxFieldsSize exceeded, received ' + self._fieldsSize + ' bytes of field data'));
@ -43,7 +43,7 @@ exports.upload = function (provider, req, res, options, cb) {
value += decoder.write(buffer); value += decoder.write(buffer);
}); });
part.on('end', function () { part.on('end', function() {
var values = fields[part.name]; var values = fields[part.name];
if (values === undefined) { if (values === undefined) {
values = [value]; values = [value];
@ -108,7 +108,11 @@ exports.upload = function (provider, req, res, options, cb) {
self.emit('fileBegin', part.name, file); self.emit('fileBegin', part.name, file);
var uploadParams = {container: container, remote: file.name, contentType: file.type}; var uploadParams = {
container: container,
remote: file.name,
contentType: file.type
};
if (file.acl) { if (file.acl) {
uploadParams.acl = file.acl; uploadParams.acl = file.acl;
} }
@ -118,7 +122,7 @@ exports.upload = function (provider, req, res, options, cb) {
self.emit('error', err); self.emit('error', err);
}); });
var endFunc = function () { var endFunc = function() {
self._flushing--; self._flushing--;
var values = files[part.name]; var values = files[part.name];
if (values === undefined) { if (values === undefined) {
@ -131,13 +135,13 @@ exports.upload = function (provider, req, res, options, cb) {
self._maybeEnd(); self._maybeEnd();
}; };
writer.on('success', function (file) { writer.on('success', function(file) {
endFunc(); endFunc();
}); });
var fileSize = 0; var fileSize = 0;
if (maxFileSize) { if (maxFileSize) {
part.on('data', function (buffer) { part.on('data', function(buffer) {
fileSize += buffer.length; fileSize += buffer.length;
file.size = fileSize; file.size = fileSize;
if (fileSize > maxFileSize) { if (fileSize > maxFileSize) {
@ -150,20 +154,31 @@ exports.upload = function (provider, req, res, options, cb) {
}); });
} }
part.on("end", function () { part.on("end", function() {
writer.end(); writer.end();
}); });
part.pipe(writer, { end: false }); part.pipe(writer, {end: false});
}; };
form.parse(req, function (err, _fields, _files) { form.parse(req, function(err, _fields, _files) {
if (err) { if (err) {
console.error(err); console.error(err);
} }
cb && cb(err, {files: files, fields: fields}); cb && cb(err, {files: files, fields: fields});
}); });
};
function handleError(res, err) {
if (err.code === 'ENOENT') {
res.type('application/json');
res.status(404).send({error: err});
return;
}
res.type('application/json');
res.status(500).send({error: err});
} }
/** /**
* Handle download from a container/file. * Handle download from a container/file.
* @param {Object} provider The storage service provider * @param {Object} provider The storage service provider
@ -174,25 +189,67 @@ exports.upload = function (provider, req, res, options, cb) {
* @callback {Function} cb Callback function. * @callback {Function} cb Callback function.
* @header storageService.download(provider, req, res, container, file, cb) * @header storageService.download(provider, req, res, container, file, cb)
*/ */
exports.download = function (provider, req, res, container, file, cb) { exports.download = function(provider, req, res, container, file, cb) {
var reader = provider.download({
var params = {
container: container || req && req.params.container, container: container || req && req.params.container,
remote: file || req && req.params.file remote: file || req && req.params.file
}); };
res.type(file); var range = null;
reader.pipe(res);
reader.on('error', function (err) { if (req) {
if (err.code === 'ENOENT') {
res.type('application/json'); if (req.headers) {
res.status(404).send({ error: err }); range = req.headers.range || '';
return;
} }
res.type('application/json'); if (range) {
res.status(500).send({ error: err }); provider.getFile(params.container, params.remote, function(err, stats) {
if (err) {
handleError(res, err);
} else {
var total = stats.size;
var parts = range.replace(/bytes=/, "").split("-")
var partialstart = parts[0]
var partialend = parts[1]
params.start = parseInt(partialstart, 10)
params.end = partialend ? parseInt(partialend, 10) : total - 1
var chunksize = (params.end - params.start) + 1
res.status(206)
res.set("Content-Range", "bytes " + params.start + "-" + params.end + "/" + total);
res.set("Accept-Ranges", "bytes");
res.set("Content-Length", chunksize);
var reader = provider.download(params);
res.type(file);
reader.pipe(res);
reader.on('error', function(err) {
handleError(res, err);
}); });
} }
});
} else {
var reader = provider.download(params);
res.type(file);
reader.pipe(res);
reader.on('error', function(err) {
handleError(res, err);
});
}
}
};

View File

@ -246,8 +246,8 @@ StorageService.prototype.upload = function(req, res, options, cb) {
* @param {Response} res HTTP response * @param {Response} res HTTP response
* @param {Function} cb Callback function * @param {Function} cb Callback function
*/ */
StorageService.prototype.download = function (container, file, res, cb) { StorageService.prototype.download = function (container, file, req, res, cb) {
return handler.download(this.client, null, res, container, file, cb); return handler.download(this.client, req, res, container, file, cb);
}; };
StorageService.modelName = 'storage'; StorageService.modelName = 'storage';
@ -321,6 +321,7 @@ StorageService.prototype.download.shared = true;
StorageService.prototype.download.accepts = [ StorageService.prototype.download.accepts = [
{arg: 'container', type: 'string', 'http': {source: 'path'}}, {arg: 'container', type: 'string', 'http': {source: 'path'}},
{arg: 'file', type: 'string', 'http': {source: 'path'}}, {arg: 'file', type: 'string', 'http': {source: 'path'}},
{arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}} {arg: 'res', type: 'object', 'http': {source: 'res'}}
]; ];
StorageService.prototype.download.http = StorageService.prototype.download.http =