node-webplay
Version:
A nodejs streaming server implementation
313 lines (250 loc) • 9.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = uplaoder;
var _fs = require("fs");
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Make a serializable error object.
*
* To create serializable errors you must re-set message so
* that it is enumerable and you must re configure the type
* property so that is writable and enumerable.
*
* @param {number} status
* @param {string} message
* @param {string} type
* @param {object} props
* @private
*/
function createError(status, message, type, props) {
var error = new Error();
// capture stack trace
Error.captureStackTrace(error, createError);
// set free-form properties
for (var prop in props) {
error[prop] = props[prop];
}
// set message
error.message = message;
// set status
error.status = status;
error.statusCode = status;
// set type
Object.defineProperty(error, "type", {
value: type,
enumerable: true,
writable: true,
configurable: true
});
return error;
}
function uplaoder(options) {
var opt = {
base_path: "./",
limit: 10 * 1024 * 1024
};
if (null != options) opt = Object.assign(opt, options);
function append(p, f, b, complete /*, start*/) {
//console.log("------- chunk buffer -> ", Buffer.isBuffer(b), b.length);
_fs2.default.mkdir(p, function (e) {
if (!e || e && e.code === "EEXIST") {
_fs2.default.appendFile(f, b, { encoding: null }, function (err) {
complete(err);
}); //do something with contents
/*
//console.log("append");
fs.open(f, 'a', (err, fd) => {
if(null != err)
{
//console.log(err);
compleate(err);
}
else
{
//console.log("write", start);
fs.write(fd, b, start, (err, written, buffer) => {
//console.log("writeback", start);
if(null != err)
{
//console.log(err);
compleate(err);
}
else
{
//console.log("WRITTEN: ", written);
fs.close(fd, (err) => {
//console.log("closed", err);
complete(err);
});
}
});
}
});
*/
} else {
//debug
complete(e);
}
});
}
return function (req, res, next) {
var stream = req;
var complete = false;
//let sync = true;
var received = 0;
var buffer = null;
var limit = opt.limit; //10MB
var length = 0;
// attach listeners
stream.on("aborted", onAborted);
stream.on("close", cleanup);
stream.on("data", onData);
stream.on("end", onEnd);
stream.on("error", onEnd);
var cr = req.headers["content-range"];
//let cont = true;
var regexp = /bytes (\d+)-(\d+)\/(\d+)/gi;
var start = 0;
var end = 0;
var total = 0;
var size = 0;
function nodone(err) {
if (err == null) res.end();else done(err);
}
if (null != cr) {
/*let m =*/cr.match(regexp);
start = RegExp.$1;
end = RegExp.$2;
total = RegExp.$3;
size = end - start;
//console.log("=======>", cr, regexp, m, start, end, total)
buffer = Buffer.alloc(size);
}
//If you pass anything to the done() function (except the string 'route'), Express regards the current request as being in error and will skip any remaining non-error handling routing and middleware functions.
function done(err) {
cleanup();
complete = true;
if (err != null) {
////console.log("next with error " + err.message);
next(err);
} else {
////console.log("next OK");
next();
}
}
function onAborted() {
if (complete) return;
done(createError(400, "request aborted", "request.aborted", {
code: "ECONNABORTED",
expected: length,
length: length,
received: received
}));
}
function onData(chunk) {
if (complete) return;
////console.log("------- chunk buffer -> ", Buffer.isBuffer(chunk));
//buffer.push(chunk);
chunk.copy(buffer, received);
received += chunk.length;
if (limit !== null && received > limit) {
done(createError(413, "request entity too large", "entity.too.large", {
limit: limit,
received: received
}));
}
}
function onEnd(err) {
if (complete) return;
if (err) return done(err);
//if(false){
if (size !== null && received !== size) {
//console.log("---->Invalid Size", size, received, length);
done(createError(400, "request size did not match content length", "request.size.invalid", {
expected: length,
length: length,
received: received
}));
} else {
/*var string = decoder
? buffer + (decoder.end() || '')
: Buffer.concat(buffer)
done(null, string)
*/
var path = opt.base_path;
////console.log(JSON.stringify(req.headers));
if (null != req.headers.owner) {
path += req.headers.owner;
path += "/";
}
var filepath = path;
if (null != req.headers["file-name"]) filepath += req.headers["file-name"];
req["uploader"] = filepath;
//let cr = req.headers['content-range'];
//let cont = true;
if (null == cr) {
//console.log('NO HEADERS');
done(createError(400, "request has invalid headers", "request.size.invalid", {
expected: size,
length: buffer.length,
received: received
}));
}
//let regexp = /bytes (\d+)-(\d+)\/(\d+)/gi;
// let m = cr.match(regexp);
//let start = RegExp.$1;
//let end = RegExp.$2;
//let total = RegExp.$3;
//let size = end - start;
////console.log("=======>", cr, regexp, m, start, end, total, received);
if (size != received) {
//console.log('ERROR INVALID SIZE', size, buffer.length);
done(createError(400, "request content-size did not match content length", "request.size.invalid", {
expected: size,
length: buffer.length,
received: received
}));
}
var fend = nodone;
if (end == total) {
//console.log("process end");
fend = done;
}
if (0 == start) {
//console.log("new file");
_fs2.default.stat(filepath, function (err /*, stat*/) {
if (err == null) {
//'File exists'
//console.log('removing', filepath);
_fs2.default.unlink(filepath, function (err) {
if (err != null) done(err);else {
append(path, filepath, buffer, fend, start);
}
});
} else if (err.code == "ENOENT") {
// file does not exist
append(path, filepath, buffer, fend);
} else {
done(err);
}
});
} else {
//console.log("process file");
append(path, filepath, buffer, fend, start);
}
}
}
function cleanup() {
// buffer = null
stream.removeListener("aborted", onAborted);
stream.removeListener("data", onData);
stream.removeListener("end", onEnd);
stream.removeListener("error", onEnd);
stream.removeListener("close", cleanup);
}
};
}
//# sourceMappingURL=server.js.map