fi-seed-component-fileman v1.1.0
fi-seed-component-fileman
Fi Seed's Fileman component
Installing
npm install --save fi-seed-component-filemanUsage
Use on fi-seed
var fileman = component('fileman');Use on Express/Node app
var fileman = require('fi-seed-component-fileman');Initialization
This component should be configured before using it:
var fileman = require('fi-seed-component-fileman');
var db = require('your-database-manager');
var app = require('express')();
var path = require('path');
fileman.configure({
tempdir: path.join(os.tmpDir(), 'my-app', 'uploads'),
stordir: path.join(process.env.HOME || process.env.USERPROFILE, 'my-app', 'storage')
});
//...
app.use(fileman.multiparser);
app.use(fileman.cleaner);
//...
app.post('/api/files/', function (req, res, next) {
var saved = [];
req.files.forEach(function (file) {
fileman.save(file, folder, function (err, fileinfo) {
if (err) {
return next(err);
}
// Save the file information (fileinfo) to a database entry or something alike...
db.files.create(fileinfo, function (err, data) {
if (err) {
return next(err);
}
saved.push(data);
if (saved.length === req.files.length) {
res.send(saved);
}
});
});
});
});
app.get('/api/files/:id', function (req, res, next) {
// Obtain the file information somehow...
db.files.findById(req.params.id, function (err, data) {
if (err) {
return next(err);
}
res.download(fileman.resolve(data.path), data.name);
});
});
//...Methods
Fileman exposes multiple methods that might be used as functions or Express middleware.
Configure
This is the initialization method and should be called before using any of it's methods:
fileman.init({
tempdir: path.join(os.tmpDir(), 'my-app', 'uploads'),
stordir: path.join(process.env.HOME || process.env.USERPROFILE, 'my-app', 'storage')
});It only receives a parameter that must be an Object with the following optional parameters:
tempdir: This can be a
Stringto the absolute path where the temporal uploaded files are saved. It defaults to:path.join(os.tmpDir(), 'fileman-uploads')stordir: This can be a
Stringto the absolute path where the files are finally stored. It defaults to:path.join(process.env.HOME || process.env.USERPROFILE, 'fileman-storage')That path might resolve to
C:\Users\[your user]\fileman-storagein Windows, to/home/[your user]/fileman-storagein Linux and to/Users/[your user]/fileman-storagein OSX.
Using a configuration module
If you wish to configure it with a module then it should look like this:
'use strict';
var path = require('path');
var os = require('os');
module.exports = {
tempdir: path.join(os.tmpDir(), 'my-app', 'uploads'),
stordir: path.join(process.env.HOME || process.env.USERPROFILE, 'my-app', 'storage')
};And then in your application, assuming it's located in [app dir]/config/fileman.js and you're calling it from a script located in [app dir]/app.js:
fileman.init(require('./config/fileman'));Multiparser
This method returns an Express middelware that intercepts POST or PUT multipart/form-data requests only. This will save the uploaded temporal files to the specified tempdir, add the uploaded files to req.files as an Array and attach the fields to req.body as an object with field names as properties. The fields are parsed as JSON whenever possible.
app.use(fileman.multiparser());In the next callback you'll receive the parameters as usual but req will now have body and files properties as an Object and Array respectively:
app.post('/api/files', function (req, res, next) {
var saved = [];
req.files.forEach(function (file) {
fileman.save(file, folder, function (err, fileinfo) {
if (err) {
return next(err);
}
// Save the file information (fileinfo) to a database entry or something alike...
db.files.create(fileinfo, function (err, data) {
if (err) {
return next(err);
}
saved.push(data);
if (saved.length === req.files.length) {
res.send(saved);
}
});
});
});
});So if the user makes a POST with multipart/form-data to /api/files, like the example above, then the req object will contain the corresponding files and fields.
Cleaner
This method returns an Express middleware that will clean all the files inside the req.files Array once the res has finished so where you declare it is not really relevant.
//...
app.use(fileman.cleaner());
//...If you do not wish to remove the temporal uploaded files, just don't use this middleware.
Save
Use this method to save a file to a path relative to the specified stordir. Useful for storing temporal uploaded files.
It must be called with three parameters:
fileinfo: This is an
Objectcontaining the file's information, not the content, composed of the following properties:- path: This is required and must be a
Stringwith the full path pointing to the file to read. - name: An optional
Stringwith the file's name.
- path: This is required and must be a
destpath: This is an optional
Stringpointing to a folder relative to thestordir. If not passed then it'll save the files directly in thestordir.done: A callback
Functionthat will receive anerrObject,nullon success, and afileinfoObject. ThefileinfoObjecthas the following properties:- name: A
Stringwith the uploaded or original file's name ornull. - path: A
Stringwith the file's path relative to thestordir. - md5: A
Stringwith the file's MD5 hash. - encoding: A
Stringwith the detected encoding. - size: A
Numberwith the detected file's size in bytes. - type: A
Stringwith the detected mimetype.
- name: A
var file = {
name: 'my-file.txt',
path: '/full/path/to/the/file/my-file.txt'
};
var folder = '/folder/relative/to/stordir';
fileman.save(file, folder, function (err, fileinfo) {
if (err) {
return next(err);
}
// File has been saved successfully
});The fileinfo Object will be like this:
{
name: String,
path: String,
md5: String,
encoding: String,
type: String,
stats: Object
}The fileinfo.stats Object is the result of a Node.js fs.stat, that looks like this:
{
dev: Number,
mode: Number,
nlink: Number,
uid: Number,
gid: Number,
rdev: Number,
blksize: Number,
ino: Number,
size: Number,
blocks: Number,
atime: Date,
mtime: Date,
ctime: Date,
birthtime: Date
}If you need to know more about fs.stats read the Node.js documentation and System stats wiki(https://en.wikipedia.org/wiki/Stat_(system_call).
Important
All paths are normalized, meaning that if you pass '..' as the folder it will save the file into the stordir's parent folder and so on.
Read
Reads a file from it's path relative to the stordir folder and returns it's read Stream:
app.get('/api/files/:id', function (req, res, end) {
// Obtain the file information somehow...
db.files.findById(req.params.id, function (err, fileinfo) {
if (err) {
return next(err);
}
fileman.read(fileinfo.path).pipe(res);
});
});It's a good practice to set the correct and recommended response headers with the corresponding information before sending it:
app.get('/api/files/:id', function (req, res, end) {
// Obtain the file information somehow...
db.files.findById(req.params.id, function (err, fileinfo) {
if (err) {
return next(err);
}
res.set({
'Content-Disposition': 'inline; filename="' + fileinfo.name + '"',
'Cache-Control': 'max-age=31536000',
'Content-Length': fileinfo.stats.size,
'Last-Modified': fileinfo.stats.mtime,
'Content-Type': fileinfo.mimetype,
'ETag': fileinfo.md5
});
fileman.read(fileinfo.path).pipe(res);
});
});Important
As you may have noticed, the fileinfo is retrieved from your database, meaning that if the files are modified, accessed or deleted outside the application then this information will be incorrect. Make sure you update your database entries accordingly.
Resolve
Use this method is to obtain the file's full path relative to the stordir folder. This can be particularly useful when using Express' res.download method:
app.get('/api/files/:id', function (req, res, end) {
// Obtain the file information somehow...
db.files.findById(req.params.id, function (err, fileinfo) {
if (err) {
return next(err);
}
res.download(fileman.resolve(fileinfo), fileinfo.name);
});
});It receives only one argument that can be a String with the relative path to the stordir or a fileinfo Object with a valid path value.