2013-06-24 21:07:12 +00:00
|
|
|
/**
|
|
|
|
* File system based on storage provider
|
|
|
|
*/
|
|
|
|
|
|
|
|
var fs = require('fs'),
|
|
|
|
path = require('path'),
|
2013-06-25 16:29:53 +00:00
|
|
|
async = require('async'),
|
|
|
|
File = require('./file').File,
|
|
|
|
Container = require('./container').Container;
|
2013-06-24 21:07:12 +00:00
|
|
|
|
|
|
|
module.exports = FileSystemProvider;
|
|
|
|
|
|
|
|
function FileSystemProvider(options) {
|
|
|
|
options = options || {};
|
|
|
|
this.root = options.root;
|
|
|
|
var stat = fs.statSync(this.root);
|
|
|
|
if (!stat.isDirectory()) {
|
|
|
|
throw new Error('Invalid directory: ' + this.root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var namePattern = new RegExp('[^' + path.sep + '/]+');
|
|
|
|
|
|
|
|
function validateName(name, cb) {
|
|
|
|
if (!name) {
|
|
|
|
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var match = namePattern.exec(name);
|
|
|
|
if (match && match.index === 0 && match[0].length === name.length) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Container related functions
|
|
|
|
FileSystemProvider.prototype.getContainers = function (cb) {
|
|
|
|
var self = this;
|
|
|
|
fs.readdir(self.root, function (err, files) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var containers = [];
|
2013-06-24 21:07:12 +00:00
|
|
|
var tasks = [];
|
|
|
|
files.forEach(function (f) {
|
|
|
|
tasks.push(fs.stat.bind(null, path.join(self.root, f)));
|
|
|
|
});
|
|
|
|
async.parallel(tasks, function (err, stats) {
|
|
|
|
if (err) {
|
|
|
|
cb && cb(err);
|
|
|
|
} else {
|
|
|
|
stats.forEach(function (stat, index) {
|
|
|
|
if (stat.isDirectory()) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var name = files[index];
|
|
|
|
var props = {name: name};
|
|
|
|
for (var p in stat) {
|
|
|
|
props[p] = stat[p];
|
|
|
|
}
|
2013-06-25 20:06:54 +00:00
|
|
|
var container = new Container(self, props);
|
2013-06-25 16:29:53 +00:00
|
|
|
containers.push(container);
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
});
|
2013-06-25 16:29:53 +00:00
|
|
|
cb && cb(err, containers);
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.createContainer = function (options, cb) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var self = this;
|
2013-06-24 21:07:12 +00:00
|
|
|
var name = options.name;
|
2013-06-25 16:29:53 +00:00
|
|
|
validateName(name, cb) && fs.mkdir(path.join(this.root, name), options, function (err) {
|
|
|
|
cb && cb(err, new Container(self, {name: name}));
|
|
|
|
});
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(containerName, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
|
|
|
|
var dir = path.join(this.root, containerName);
|
|
|
|
fs.readdir(dir, function (err, files) {
|
|
|
|
var tasks = [];
|
|
|
|
files.forEach(function (f) {
|
2013-06-24 22:53:14 +00:00
|
|
|
tasks.push(fs.unlink.bind(null, path.join(dir, f)));
|
2013-06-24 21:07:12 +00:00
|
|
|
});
|
|
|
|
async.parallel(tasks, function (err) {
|
|
|
|
if (err) {
|
|
|
|
cb && cb(err);
|
|
|
|
} else {
|
|
|
|
fs.rmdir(dir, cb);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.getContainer = function (containerName, cb) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var self = this;
|
|
|
|
if (!validateName(containerName, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var dir = path.join(this.root, containerName);
|
2013-06-25 16:29:53 +00:00
|
|
|
fs.stat(dir, function (err, stat) {
|
|
|
|
var container = null;
|
|
|
|
if (!err) {
|
|
|
|
var props = {name: containerName};
|
|
|
|
for (var p in stat) {
|
|
|
|
props[p] = stat[p];
|
|
|
|
}
|
|
|
|
container = new Container(self, props);
|
|
|
|
}
|
|
|
|
cb && cb(err, container);
|
|
|
|
});
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// File related functions
|
|
|
|
FileSystemProvider.prototype.upload = function (options, cb) {
|
|
|
|
var container = options.container;
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(container, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var file = options.remote;
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(file, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var filePath = path.join(this.root, container, file);
|
|
|
|
|
|
|
|
var fileOpts = {flags: 'w+',
|
|
|
|
encoding: null,
|
|
|
|
mode: 0666 };
|
|
|
|
|
|
|
|
return fs.createWriteStream(filePath, fileOpts);
|
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.download = function (options, cb) {
|
|
|
|
var container = options.container;
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(container, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var file = options.remote;
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(file, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
|
|
|
|
var filePath = path.join(this.root, container, file);
|
|
|
|
|
|
|
|
var fileOpts = {flags: 'r',
|
|
|
|
autoClose: true };
|
|
|
|
|
|
|
|
return fs.createReadStream(filePath, fileOpts);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-25 20:06:54 +00:00
|
|
|
FileSystemProvider.prototype.getFiles = function (container, download, cb) {
|
|
|
|
if (typeof download === 'function' && !(download instanceof RegExp)) {
|
|
|
|
cb = download;
|
|
|
|
download = false;
|
|
|
|
}
|
2013-06-25 16:29:53 +00:00
|
|
|
var self = this;
|
|
|
|
if (!validateName(container, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var dir = path.join(this.root, container);
|
2013-06-25 16:29:53 +00:00
|
|
|
fs.readdir(dir, function (err, entries) {
|
|
|
|
var files = [];
|
2013-06-24 21:07:12 +00:00
|
|
|
var tasks = [];
|
2013-06-25 16:29:53 +00:00
|
|
|
entries.forEach(function (f) {
|
2013-06-24 22:53:14 +00:00
|
|
|
tasks.push(fs.stat.bind(null, path.join(dir, f)));
|
2013-06-24 21:07:12 +00:00
|
|
|
});
|
|
|
|
async.parallel(tasks, function (err, stats) {
|
|
|
|
if (err) {
|
|
|
|
cb && cb(err);
|
|
|
|
} else {
|
|
|
|
stats.forEach(function (stat, index) {
|
|
|
|
if (stat.isFile()) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var props = {container: container, name: entries[index]};
|
|
|
|
for (var p in stat) {
|
|
|
|
props[p] = stat[p];
|
|
|
|
}
|
|
|
|
var file = new File(self, props);
|
|
|
|
files.push(file);
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
});
|
2013-06-25 16:29:53 +00:00
|
|
|
cb && cb(err, files);
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.getFile = function (container, file, cb) {
|
2013-06-25 16:29:53 +00:00
|
|
|
var self = this;
|
|
|
|
if (!validateName(container, cb)) return;
|
|
|
|
if (!validateName(file, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
var filePath = path.join(this.root, container, file);
|
2013-06-25 16:29:53 +00:00
|
|
|
fs.stat(filePath, function (err, stat) {
|
2013-06-25 17:44:37 +00:00
|
|
|
var f = null;
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!err) {
|
|
|
|
var props = {container: container, name: file};
|
|
|
|
for (var p in stat) {
|
|
|
|
props[p] = stat[p];
|
|
|
|
}
|
2013-06-25 17:44:37 +00:00
|
|
|
f = new File(self, props);
|
2013-06-25 16:29:53 +00:00
|
|
|
}
|
2013-06-25 17:44:37 +00:00
|
|
|
cb && cb(err, f);
|
2013-06-25 16:29:53 +00:00
|
|
|
});
|
2013-06-24 21:07:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileSystemProvider.prototype.removeFile = function (container, file, cb) {
|
2013-06-25 16:29:53 +00:00
|
|
|
if (!validateName(container, cb)) return;
|
|
|
|
if (!validateName(file, cb)) return;
|
2013-06-24 21:07:12 +00:00
|
|
|
|
|
|
|
var filePath = path.join(this.root, container, file);
|
|
|
|
fs.unlink(filePath, cb);
|
|
|
|
}
|