total4
Version:
Total.js framework v4
1,259 lines (1,004 loc) • 28.9 kB
JavaScript
const Readable = require('stream').Readable;
const Path = require('path');
const Fs = require('fs');
const IMAGES = { jpg: 1, png: 1, gif: 1, svg: 1, jpeg: 1, heic: 1, heif: 1, webp: 1, tiff: 1, bmp: 1 };
const HEADERSIZE = 2000;
const MKDIR = { recursive: true };
const REGCLEAN = /^[\s]+|[\s]+$/g;
const GZIPFILE = { memLevel: 9 };
const ENCODING = 'utf8';
const CONCAT = [null, null];
const REG_RANGE = /bytes=/;
function FileDB(name, directory) {
var t = this;
t.name = name;
// t.directory = directory;
// t.logger = directory + '/files.log';
t.cache = {};
t.total = 0;
t.size = 0;
t.ext = '.file';
t.pause = false;
ON('service', function(counter) {
if (counter % 10)
t.cache = {};
});
t.retrysave = function(id, name, filename, callback, custom, expire, headers) {
t._save(id, name, filename, callback, custom, expire, headers);
};
t.retryread = function(id, callback, nostream) {
t._read(id, callback, nostream);
};
t.storage(directory);
}
const FP = FileDB.prototype;
FP.storage = function(value) {
var self = this;
self.cache = {};
self.directory = value;
self.logger = Path.join(value, 'files.log');
return self;
};
FP.count = function(callback) {
var self = this;
NOSQL('~' + self.logger).scalar('sum', 'size').callback(function(err, response) {
response.size = response.sum;
self.size = response.size;
self.total = response.count;
response.sum = undefined;
callback && callback(err, response);
});
return self;
};
FP.makedirectory = function(id) {
return Path.join(this.directory, U.groupify(id));
};
FP.readfilename = function(id) {
var self = this;
var directory = self.makedirectory(id);
return Path.join(directory, id + '.file');
};
FP.savejson = function(id, value, callback, custom, expire) {
return this.save(id, id + '.json', Buffer.from(JSON.stringify(value), 'utf8'), callback, custom, expire);
};
FP.readjson = function(id, callback) {
return this.read(id, function(err, meta) {
if (err) {
callback(err);
return;
}
var buffer = [];
meta.stream.on('data', chunk => buffer.push(chunk));
meta.stream.on('end', function() {
meta.stream = null;
callback(null, Buffer.concat(buffer).toString('utf8').parseJSON(true), meta);
});
});
};
FP.save = FP.insert = function(id, name, filename, callback, custom, expire, headers) {
var self = this;
if (callback && typeof(callback) !== 'function') {
headers = expire;
expire = custom;
custom = callback;
callback = null;
}
if (callback)
return self._save(id, name, filename, callback, custom, expire, headers);
else
return new Promise((resolve, reject) => self._save(id, name, filename, (err, res) => err ? reject(err) : resolve(res), custom, expire, headers));
};
FP._save = function(id, name, filename, callback, custom, expire, headers) {
var self = this;
if (self.pause) {
setTimeout(self.retrysave, 500, id, name, filename, callback, custom, expire, headers);
return self;
}
var directory = self.makedirectory(id);
var filenameto = Path.join(directory, id + '.file');
var index = name.lastIndexOf('/');
if (index !== -1)
name = name.substring(index + 1);
if (self.cache[directory]) {
if (typeof(filename) === 'string' && filename[0] === 'h' && filename[1] === 't' && filename[7] === '/') {
// URL address
var opt = {};
opt.url = filename;
opt.custom = true;
opt.headers = headers;
opt.callback = function(err, response) {
if (err) {
callback(err);
return;
}
if (response.status < 400)
self.saveforce(id, name, response.stream, filenameto, callback, custom, expire);
else
callback(U.httpstatus(response.status));
};
REQUEST(opt);
} else
self.saveforce(id, name, filename, filenameto, callback, custom, expire);
} else {
Fs.mkdir(directory, MKDIR, function(err) {
if (err)
callback(err);
else {
self.cache[directory] = 1;
if (typeof(filename) === 'string' && filename[0] === 'h' && filename[1] === 't' && filename[7] === '/') {
// URL address
var opt = {};
opt.url = filename;
opt.custom = true;
opt.headers = headers;
opt.callback = function(err, response) {
if (err) {
callback(err);
return;
}
if (response.status < 400)
self.saveforce(id, name, response.stream, filenameto, callback, custom, expire);
else
callback(U.httpstatus(response.status));
};
REQUEST(opt);
} else
self.saveforce(id, name, filename, filenameto, callback, custom, expire);
}
});
}
return self;
};
FP.saveforce = function(id, name, filename, filenameto, callback, custom, expire) {
if (!callback)
callback = NOOP;
F.stats.performance.open++;
var isbuffer = filename instanceof Buffer;
var self = this;
var header = Buffer.alloc(HEADERSIZE, ' ');
var reader = isbuffer ? null : filename instanceof Readable ? filename : Fs.createReadStream(filename);
var writer = Fs.createWriteStream(filenameto);
var ext = framework_utils.getExtension(name);
var meta = { name: name, size: 0, ext: ext, custom: custom, type: U.getContentType(ext) };
var tmp;
writer.write(header, 'binary');
if (IMAGES[meta.ext]) {
if (isbuffer) {
switch (meta.ext) {
case 'gif':
tmp = framework_image.measureGIF(filename);
break;
case 'png':
tmp = framework_image.measurePNG(filename);
break;
case 'jpg':
case 'jpeg':
tmp = framework_image.measureJPG(filename);
break;
case 'svg':
tmp = framework_image.measureSVG(filename);
break;
}
} else {
reader.once('data', function(buffer) {
switch (meta.ext) {
case 'gif':
tmp = framework_image.measureGIF(buffer);
break;
case 'png':
tmp = framework_image.measurePNG(buffer);
break;
case 'jpg':
case 'jpeg':
tmp = framework_image.measureJPG(buffer);
break;
case 'svg':
tmp = framework_image.measureSVG(buffer);
break;
}
});
}
}
if (isbuffer) {
writer.end(filename);
} else {
reader.pipe(writer);
if (typeof(filename) !== 'string')
reader.resume();
}
CLEANUP(writer, function() {
Fs.open(filenameto, 'r+', function(err, fd) {
if (err) {
// Unhandled error
callback(err);
return;
}
if (tmp) {
meta.width = tmp.width;
meta.height = tmp.height;
}
meta.size = writer.bytesWritten - HEADERSIZE;
meta.date = NOW = new Date();
if (expire)
meta.expire = NOW.add(expire);
self.total++;
self.size += meta.size;
if (meta.name.length > 250)
meta.name = meta.name.substring(0, 250);
header.write(JSON.stringify(meta));
// Update header
Fs.write(fd, header, 0, header.length, 0, function(err) {
if (err) {
callback(err);
Fs.close(fd, NOOP);
} else {
meta.id = id;
Fs.appendFile(self.logger, JSON.stringify(meta) + '\n', NOOP);
Fs.close(fd, () => callback(null, meta));
}
});
});
});
};
FP.read = function(id, callback, nostream) {
var self = this;
if (callback)
return self._read(id, callback, nostream);
else
return new Promise((resolve, reject) => self._read(id, (err, res) => err ? reject(err) : resolve(res), nostream));
};
FP.clone = function(id, newid, callback) {
var self = this;
if (typeof(newid) === 'function') {
callback = newid;
newid = UID();
}
if (callback)
return self._clone(id, newid, callback);
else
return new Promise((resolve, reject) => self._clone(id, newid, (err, res) => err ? reject(err) : resolve(res)));
};
FP._clone = function(id, newid, callback) {
var self = this;
if (self.pause) {
setTimeout(self._clone, 500, id, newid, callback);
return self;
}
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.open(filename, 'r', function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
F.stats.performance.open++;
var str = buffer.toString('utf8').replace(REGCLEAN, '');
if (!str) {
// Invalid file
Fs.close(fd, function() {
if (buffer.length === HEADERSIZE)
Fs.unlink(filename, NOOP);
});
callback('File not found');
return;
}
var meta = str.parseJSON(true);
if (!meta) {
Fs.close(fd, NOOP);
callback('Invalid file');
return;
}
Fs.close(fd, NOOP);
meta.id = newid;
if (meta.expire && meta.expire < NOW) {
callback('File is expired');
return;
}
var directory = self.makedirectory(newid);
var filenamenew = Path.join(directory, newid + '.file');
if (self.cache[directory]) {
Fs.copyFile(filename, filenamenew, function(err) {
if (!err)
Fs.appendFile(self.logger, JSON.stringify(meta) + '\n', NOOP);
callback && callback(err, meta);
});
} else {
Fs.mkdir(directory, MKDIR, function(err) {
if (err) {
callback(err);
return;
}
self.cache[directory] = 1;
Fs.copyFile(filename, filenamenew, function(err) {
if (!err)
Fs.appendFile(self.logger, JSON.stringify(meta) + '\n', NOOP);
callback && callback(err, meta);
});
});
}
});
});
};
FP.copy = function(id, path, callback) {
var self = this;
if (callback)
return self._copy(id, path, callback);
else
return new Promise((resolve, reject) => self._copy(id, path, (err, res) => err ? reject(err) : resolve(res)));
};
FP._copy = function(id, path, callback) {
var self = this;
if (self.pause) {
setTimeout(self._copy, 500, id, path, callback);
return self;
}
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.open(filename, 'r', function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
var str = buffer.toString('utf8').replace(REGCLEAN, '');
if (!str) {
// Invalid file
Fs.close(fd, function() {
if (buffer.length === HEADERSIZE)
Fs.unlink(filename, NOOP);
});
callback('File not found');
return;
}
var meta = str.parseJSON(true);
if (!meta) {
Fs.close(fd, NOOP);
callback('Invalid file');
return;
}
meta.id = id;
if (meta.expire && meta.expire < NOW) {
Fs.close(fd, NOOP);
callback('File is expired');
return;
}
F.stats.performance.open++;
var reader = Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
var writer = Fs.createWriteStream(path.includes('.') ? path : Path.join(path, meta.name));
reader.pipe(writer);
CLEANUP(reader, function() {
callback(err, meta);
Fs.close(fd, NOOP);
});
});
});
};
FP._read = function(id, callback, nostream) {
var self = this;
if (self.pause) {
setTimeout(self.retryread, 500, id, callback, nostream);
return self;
}
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.open(filename, 'r', function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
var str = buffer.toString('utf8').replace(REGCLEAN, '');
if (!str) {
// Invalid file
Fs.close(fd, function() {
if (buffer.length === HEADERSIZE)
Fs.unlink(filename, NOOP);
});
callback('File not found');
return;
}
var meta = str.parseJSON(true);
if (!meta) {
Fs.close(fd, NOOP);
callback('Invalid file');
return;
}
meta.id = id;
if (meta.expire && meta.expire < NOW) {
Fs.close(fd, NOOP);
callback('File is expired');
return;
}
if (!nostream) {
F.stats.performance.open++;
meta.stream = Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
CLEANUP(meta.stream, () => Fs.close(fd, NOOP));
} else
Fs.close(fd, NOOP);
callback(err, meta);
});
});
return self;
};
FP.readbuffer = function(id, callback) {
var self = this;
if (callback)
return self._readbuffer(id, callback);
else
return new Promise((resolve, reject) => self._readbuffer(id, (err, res) => err ? reject(err) : resolve(res)));
};
FP._readbuffer = function(id, callback) {
var self = this;
if (self.pause) {
setTimeout(self._readbuffer, 500, id, callback);
return self;
}
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.open(filename, 'r', function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
var meta = buffer.toString('utf8').replace(REGCLEAN, '').parseJSON(true);
meta.id = id;
if (meta.expire && meta.expire < NOW) {
Fs.close(fd, NOOP);
callback('File is expired');
return;
}
buffer = [];
F.stats.performance.open++;
var stream = Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
stream.on('data', chunk => buffer.push(chunk));
CLEANUP(stream, function() {
Fs.close(fd, NOOP);
callback(err, Buffer.concat(buffer), meta);
});
});
});
return self;
};
FP.browse = function(callback) {
var db = NOSQL('~' + this.logger).find();
if (callback)
db.$callback = callback;
return db;
};
FP.move = function(id, newid, callback) {
var self = this;
if (callback)
return self._move(id, newid, callback);
else
return new Promise((resolve, reject) => self._move(id, newid, (err, res) => err ? reject(err) : resolve(res)));
};
FP._move = function(id, newid, callback) {
var self = this;
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.lstat(filename, function(err) {
if (err) {
callback(err);
return;
}
var directory = self.makedirectory(newid);
var filenamenew = Path.join(directory, newid + '.file');
if (self.cache[directory]) {
Fs.rename(filename, filenamenew, err => callback && callback(err));
} else {
Fs.mkdir(directory, MKDIR, function(err) {
if (err) {
callback(err);
return;
}
self.cache[directory] = 1;
Fs.rename(filename, filenamenew, err => callback && callback(err));
});
}
});
return self;
};
FP.rename = function(id, newname, callback) {
var self = this;
if (callback)
return self._rename(id, newname, callback);
else
return new Promise((resolve, reject) => self._rename(id, newname, (err, res) => err ? reject(err) : resolve(res)));
};
FP._rename = function(id, newname, callback) {
var self = this;
var filename = Path.join(self.makedirectory(id), id + '.file');
F.stats.performance.open++;
Fs.open(filename, 0o666, function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
var meta = buffer.toString('utf8').replace(REGCLEAN, '').parseJSON(true);
meta.name = newname;
if (meta.name.length > 250)
meta.name = meta.name.substring(0, 250);
buffer = Buffer.alloc(HEADERSIZE, ' ');
buffer.write(JSON.stringify(meta));
// Update header
Fs.write(fd, buffer, 0, buffer.length, 0, function(err) {
if (err) {
callback(err);
Fs.close(fd, NOOP);
} else {
meta.id = id;
NOSQL('~' + self.logger).modify(meta).id(id);
// Fs.appendFile(self.logger, JSON.stringify(meta) + '\n', NOOP);
Fs.close(fd, () => callback(null, meta));
}
});
});
});
return self;
};
FP.remove = function(id, callback) {
var self = this;
if (callback)
return self._remove(id, callback);
else
return new Promise((resolve, reject) => self._remove(id, (err, res) => err ? reject(err) : resolve(res)));
};
FP._remove = function(id, callback) {
var self = this;
var filename = Path.join(self.makedirectory(id), id + '.file');
Fs.unlink(filename, function(err) {
NOSQL('~' + self.logger).remove().id(id);
// Fs.appendFile(self.logger, JSON.stringify({ id: id, removed: true, date: NOW = new Date() }) + '\n', NOOP);
callback && callback(err);
});
return self;
};
FP.clean = function(callback) {
var self = this;
if (callback)
return self._clean(callback);
else
return new Promise((resolve, reject) => self._clean((err, res) => err ? reject(err) : resolve(res)));
};
FP._clean = function(callback) {
var self = this;
var db = NOSQL('~' + self.logger);
db.find().where('expire', '<', NOW).callback(function(err, files) {
if (err || !files || !files.length) {
callback && callback(err, 0);
return;
}
var id = [];
for (var i = 0; i < files.length; i++)
id.push(files[i].id);
db.remove().in('id', id);
files.wait(function(item, next) {
var filename = Path.join(self.makedirectory(item.id), item.id + '.file');
Fs.unlink(filename, next);
}, function() {
FP.count();
db.clean();
callback && callback(err, files.length);
});
});
return self;
};
FP.backup = function(filename, callback) {
var self = this;
if (callback)
return self._backup(filename, callback);
else
return new Promise((resolve, reject) => self._backup(filename, (err, res) => err ? reject(err) : resolve(res)));
};
FP._backup = function(filename, callback) {
var self = this;
var writer = typeof(filename) === 'string' ? Fs.createWriteStream(filename) : filename;
var totalsize = 0;
var counter = 0;
var padding = 50;
writer.on('finish', function() {
callback && callback(null, { filename: filename, files: counter, size: totalsize });
});
Fs.readdir(self.directory, function(err, response) {
if (err) {
callback(err);
return;
}
for (var dir of response) {
if (dir.length === 4) {
var tmp = Buffer.from(('/' + dir + '/').padRight(padding) + ': #\n', ENCODING);
writer.write(tmp);
totalsize += tmp.length;
}
}
response.wait(function(item, next) {
if (item.length !== 4) {
next();
return;
}
var dir = Path.join(self.directory, item);
Fs.readdir(dir, function(err, response) {
response.wait(function(name, next) {
var filename = Path.join(dir, name);
var data = Buffer.alloc(0);
var tmp = Buffer.from(('/' + Path.join(item, name)).padRight(padding) + ': ');
totalsize += tmp.length;
writer.write(tmp);
Fs.createReadStream(filename).pipe(F.Zlib.createGzip(GZIPFILE)).on('data', function(chunk) {
CONCAT[0] = data;
CONCAT[1] = chunk;
data = Buffer.concat(CONCAT);
var remaining = data.length % 3;
if (remaining) {
var tmp = data.slice(0, data.length - remaining).toString('base64');
writer.write(tmp, ENCODING);
data = data.slice(data.length - remaining);
totalsize += tmp.length;
}
}).on('end', function() {
var tmp = data.length ? data.toString('base64') : '';
data.length && writer.write(tmp);
writer.write('\n', ENCODING);
totalsize += tmp.length + 1;
counter++;
setImmediate(next);
}).on('error', function() {
setImmediate(next);
});
}, next);
});
}, () => writer.end());
});
};
FP.restore = function(filename, callback) {
var self = this;
if (callback)
return self._restore(filename, callback);
else
return new Promise((resolve, reject) => self._restore(filename, (err, res) => err ? reject(err) : resolve(res)));
};
FP._restore = function(filename, callback) {
var self = this;
self.pause = true;
self.clear(function() {
self.pause = true;
RESTORE(filename, self.directory, function(err, meta) {
self.cache = {};
self.pause = false;
callback && callback(err, meta);
});
});
};
FP.drop = FP.clear = function(callback) {
var self = this;
if (callback)
return self._clear(callback);
else
return new Promise((resolve, reject) => self._clear((err, res) => err ? reject(err) : resolve(res)));
};
FP._clear = function(callback) {
var self = this;
var count = 0;
self.pause = true;
Fs.readdir(self.directory, function(err, response) {
if (err) {
callback(err);
return;
}
Fs.unlink(self.logger, NOOP);
response.wait(function(item, next) {
var dir = Path.join(self.directory, item);
Fs.readdir(dir, function(err, response) {
if (response instanceof Array) {
count += response.length;
response.wait((file, next) => Fs.unlink(Path.join(self.directory, item, file), next), () => Fs.rmdir(dir, next));
} else
next();
});
}, function() {
Fs.unlink(self.logger, NOOP);
self.pause = false;
self.cache = {};
callback && callback(null, count);
});
});
return self;
};
FP.stream = function(onfile, callback, workers) {
var self = this;
Fs.readdir(self.directory, function(err, response) {
if (err) {
callback();
return;
}
var count = 0;
response.wait(function(item, next) {
if (item.length !== 4) {
next();
return;
}
Fs.readdir(Path.join(self.directory, item), function(err, files) {
if (files instanceof Array) {
files.wait(function(item, next) {
var id = item.substring(0, item.lastIndexOf('.'));
self.read(id, function(err, meta) {
if (meta) {
meta.id = id;
meta.index = count++;
onfile(meta, next);
} else
next();
}, true);
}, next, workers);
} else
next();
});
}, callback);
});
return self;
};
FP.browse2 = function(callback) {
var self = this;
if (callback)
return self._browse2(callback);
else
return new Promise((resolve, reject) => self._browse2((err, res) => err ? reject(err) : resolve(res)));
};
FP._browse2 = function(callback) {
var self = this;
var files = [];
self.stream(function(item, next) {
files.push(item);
next();
}, () => callback(null, files), 5);
return self;
};
FP.rebuild = function(callback) {
var self = this;
if (callback)
return self._rebuild(callback);
else
return new Promise((resolve, reject) => self._rebuild((err, res) => err ? reject(err) : resolve(res)));
};
FP._rebuild = function(callback) {
var self = this;
self.browse2(function(err, files) {
self.pause = true;
Fs.unlink(self.logger, NOOP);
var builder = [];
self.size = 0;
self.total = 0;
for (var i = 0; i < files.length; i++) {
var item = files[i];
self.size += item.size;
self.total++;
builder.push(JSON.stringify(item));
}
builder.limit(500, (items, next) => Fs.appendFile(self.logger, items.join('\n'), next), function() {
Fs.appendFile(self.logger, '\n', NOOP);
self.pause = false;
callback && callback();
});
});
return self;
};
FP.count2 = function(callback) {
var self = this;
if (callback)
return self._count2(callback);
else
return new Promise((resolve, reject) => self._count2((err, res) => err ? reject(err) : resolve(res)));
};
FP._count2 = function(callback) {
var self = this;
var count = 0;
Fs.readdir(self.directory, function(err, response) {
response.wait(function(item, next) {
Fs.readdir(Path.join(self.directory, item), function(err, response) {
if (response instanceof Array)
count += response.length;
next();
});
}, () => callback(null, count));
});
return self;
};
function jsonparser(key, value) {
return typeof(value) === 'string' && value.isJSONDate() ? new Date(value) : value;
}
FP.readmeta = function(id, callback, keepfd) {
var self = this;
if (callback)
return self._readmeta(id, callback, keepfd);
else
return new Promise((resolve, reject) => self._readmeta(id, (err, res) => err ? reject(err) : resolve(res), keepfd));
};
FP._readmeta = function(id, callback, keepfd) {
var self = this;
var filename = Path.join(self.makedirectory(id), id + self.ext);
F.stats.performance.open++;
Fs.open(filename, function(err, fd) {
if (err) {
callback(err);
return;
}
var buffer = Buffer.alloc(HEADERSIZE);
Fs.read(fd, buffer, 0, buffer.length, 0, function(err, bytes, buffer) {
if (err) {
Fs.close(fd, NOOP);
callback(err);
return;
}
var json = buffer.toString('utf8').replace(REGCLEAN, '');
try {
json = JSON.parse(json, jsonparser);
} catch (e) {
Fs.close(fd, NOOP);
callback(e, null, filename);
return;
}
if (!keepfd)
Fs.close(fd, NOOP);
callback(null, json, filename, fd);
});
});
return self;
};
FP.image = function(id, callback) {
var self = this;
if (callback)
return self._image(id, callback);
else
return new Promise((resolve, reject) => self._image(id, (err, res) => err ? reject(err) : resolve(res)));
};
FP._image = function(id, callback) {
var self = this;
self.readmeta(id, function(err, obj, filename, fd) {
if (err) {
callback(err);
return;
}
var stream = Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
var image = Image.load(stream);
stream.$totalfd = fd;
callback(err, image, obj);
CLEANUP(stream);
}, true);
return self;
};
FP.res = function(res, options, checkcustom) {
var self = this;
var req = res.req;
if (RELEASE && req.$key && F.temporary.notfound[req.$key] !== undefined) {
res.throw404();
return res;
}
var id = options.id || '';
self.readmeta(id, function(err, obj, filename, fd) {
if (err || (obj.expire && obj.expire < NOW) || (checkcustom && checkcustom(obj) == false)) {
if (RELEASE)
F.temporary.notfound[F.createTemporaryKey(req)] = true;
fd && Fs.close(fd, NOOP);
res.throw404();
return;
}
F.stats.performance.open++;
var utc = obj.date ? obj.date.toUTCString() : '';
if (!options.download && req.headers['if-modified-since'] === utc) {
Fs.close(fd, NOOP);
res.extension = obj.ext || framework_utils.getExtension(obj.name);
F.$file_notmodified(res, utc);
} else {
if (RELEASE && req.$key && F.temporary.path[req.$key]) {
Fs.close(fd, NOOP);
res.$file();
return res;
}
F.stats.performance.open++;
res.options.type = obj.type;
res.options.lastmodified = true;
!options.headers && (options.headers = {});
if (options.download) {
res.options.download = options.download === true ? obj.name : typeof(options.download) === 'function' ? options.download(obj.name, obj.type) : options.download;
} else
options.headers['Last-Modified'] = utc;
if (obj.width && obj.height) {
options.headers['X-Width'] = obj.width;
options.headers['X-Height'] = obj.height;
}
options.headers['X-Size'] = obj.size;
res.options.headers = options.headers;
res.options.done = options.done;
Fs.close(fd, NOOP);
if (options.image) {
res.options.stream = { filename: filename, start: HEADERSIZE, custom: true };
res.options.make = options.make;
res.options.cache = options.cache !== false;
res.options.persistent = false;
res.$image();
} else {
var range = req.headers.range;
if (range) {
var arr = range.replace(REG_RANGE, '').split('-');
var beg = (arr[0] ? +arr[0] : 0);
var end = (arr[1] ? +arr[1] : 0);
if (isNaN(beg) || isNaN(end)) {
res.throw404();
return;
}
if (end <= 0)
end = beg + ((1024 * 1024) * 5); // 5 MB
if (beg > end) {
beg = 0;
end = obj.size - 1;
}
if (end > obj.size)
end = obj.size - 1;
if (beg >= end || beg < 0) {
res.throw404();
return;
}
var length = (end - beg) + 1;
res.options.code = 206;
res.options.headers = {};
res.options.headers['Cache-Control'] = DEBUG ? 'private, no-cache, no-store, max-age=0' : 'public, max-age=11111111';
res.options.headers['Accept-Ranges'] = 'bytes';
res.options.headers['Content-Length'] = length;
res.options.headers['Content-Range'] = 'bytes ' + beg + '-' + end + '/' + obj.size;
res.options.stream = Fs.createReadStream(filename, { flags: 'r', mode: '0666', autoClose: true, start: HEADERSIZE + beg, end: end + HEADERSIZE });
} else
res.options.stream = Fs.createReadStream(filename, { start: HEADERSIZE });
res.options.compress = options.nocompress ? false : true;
res.$stream();
}
}
}, true);
};
FP.readbase64 = function(id, callback) {
var self = this;
if (callback)
return self._readbase64(id, callback);
else
return new Promise((resolve, reject) => self._readbase64(id, (err, res) => err ? reject(err) : resolve(res)));
};
FP._readbase64 = function(id, callback) {
var self = this;
self._readbuffer(id, (err, buffer, meta) => callback(err, buffer ? buffer.toString('base64') : null, meta));
return self;
};
exports.FileDB = function(name, directory) {
return new FileDB(name, directory);
};