Build a blog with Areto Node.js framework

File model

The File model provides a file upload. It supports asynchronous uploading from web forms. Downloaded files are not used directly, but only as a source of data in other models.

Disable the updatedAttr attribute of the timestamp behavior. Downloaded files can not be edited, but only either deleted or used for re-saving in some models.

The STORE_DIR constant creates an absolute path to the directory of the file allocation.


const Base = require('areto/db/ActiveRecord');
const path = require('path');

module.exports = class File extends Base {
  static getConstants () {
    return {
      TABLE: 'file',
      ATTRS: [
      RULES: [
        ['file', 'required'],
        ['file', 'file']
      BEHAVIORS: {
        timestamp: {
          Class: require('areto/behaviors/Timestamp'),
          updatedAttr: false
      STORE_DIR: path.join(__dirname, '../uploads/temp')

const fs = require('fs');
const multer = require('multer');
const mkdirp = require('mkdirp');
const CommonHelper = require('areto/helper/CommonHelper');

The findExpired method finds all files older than the specified period.


static findExpired (elapsedSeconds = 3600) {
  let expired = new Date( - parseInt(timeout) * 1000);
  return this.find(['<', 'updatedAt', expired]);

The model title is formed from the original file name and stored name.


getTitle () {
  return `${this.get('originalName')} (${this.get('filename')})`;

The isImage method recognizes a image file.


isImage () {
  return this.get('mime').indexOf('image') === 0;

The getPath method returns absolute path of a saved file.


getPath () {
  return path.join(this.STORE_DIR, this.get('filename'));

The upload method saves a file to the server, using the multer npm-module.


upload (controller, cb) {
  let req = controller.req;
    cb => multer({
      'storage': multer.diskStorage({
        'destination': this.generateStoreDir.bind(this),
        'filename': this.generateFilename.bind(this)
    }).single('file')(req, controller.res, cb),
    cb => {
      this.populateFileStats(req.file, controller);
      this.set('file', this.getFileStats());;
  ], cb);

The generateStoreDir, generateFilename methods create a directory and generate a file name to save.


generateStoreDir (req, file, cb) {
  mkdirp(this.STORE_DIR, err => cb(err, this.STORE_DIR));

generateFilename (req, file, cb) {
  cb(null, + CommonHelper.getRandom(11, 99));

The populateFileStats method extracts values of a file and request.


populateFileStats (file, controller) {
  this.set('userId', controller.user.getId());
  this.set('originalName', file.originalname);
  this.set('filename', file.filename);
  this.set('mime', file.mimetype);
  this.set('extension', path.extname(file.originalname).substring(1).toLowerCase());
  this.set('size', file.size);
  this.set('ip', controller.req.ip);

The getFileStats method returns the data needed to validate a uploaded file.


getFileStats () {
  return {
    model: this,
    path: this.getPath(),
    size: this.get('size'),
    extension: this.get('extension'),
    mime: this.get('mime')

The afterDelete handler deletes a file from the file system. It is called after the removal of the model.


afterDelete (cb) {
    cb => super.afterRemove(cb),
    cb => fs.unlink(this.getPath(), cb)
  ], cb);