Add range support
This commit is contained in:
parent
a204980c82
commit
fdb4c0464e
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
Loading…
Reference in New Issue