Skip to content

Constructor

dr.dimitru edited this page Jul 22, 2018 · 37 revisions
new FilesCollection([config]) [Isomorphic]

Initialize FilesCollection collection.

Param/Type Locus Description Default Comment
config {Object} Isomorphic [Optional] {} See all options below
config.storagePath {String|Function} Server Storage path on file system function { return 'assets/app/uploads'; }
Always converted into the function since v1.7.4
Relative to running script
If Function is passed it must return String, arguments:
  • defaultPath - Default recommended path
Context is current FilesCollction instance.

Note: When running in development mode files stored at a relative path (within the Meteor project) are silently removed when Meteor is restarted.

To preserve files in development mode store them outside of the Meteor application, e.g. /data/Meteor/uploads/

The Meteor-Files package operates on the host filesystem, unlike Meteor Assets. When a relative path is specified for config.storagePath (path starts with ./ or no slash) files will be located relative to the assets folder.

When an absolute path is used (path starts with /) files will be located starting at the root of the filesystem.

If using MeteorUp, Docker volumes has to be created in mup.json, see Usage on MeteorUp
config.collection {Mongo.Collection} Isomorphic Mongo.Collection Instance You can pass your own Mongo Collection instance {collection: new Mongo.Collection('myFiles')}
config.collectionName {String} Isomorphic Collection name MeteorUploadFiles
config.continueUploadTTL {Number} Server Time in seconds, during upload may be continued, default 3 hours (10800 seconds) 10800 (3 hours) If upload is not continued during this time, memory used for this upload will be freed. And uploaded chunks is removed. Server will no longer wait for upload, and if upload will be tied to be continued - Server will return 408 Error (Can't continue upload, session expired. Start upload again.)
config.ddp {Object} Client Custom DDP connection for Collection. Object returned form DDP.connect() Meteor (The default DDP connection)
config.cacheControl {String} Server Set Cache-Control header public, max-age=31536000, s-maxage=31536000
config.responseHeaders {Object|Function} Server Allows to change default response headers Default Function We recommend to keep original function structure, with your modifications, see example altering default headers
DEPRECATED config.throttle {Number|false} Server DEPRECATED Throttle download speed in bps - TEMPORARILY DEPRECATED SINCE v1.9.0
config.downloadRoute {String} Isomorphic Server Route used to retrieve files /cdn/storage
config.schema {Object} Isomorphic Collection Schema Default Schema For more info read Schema docs
config.chunkSize {Number} Isomorphic Upload & Serve (for 206 responce) chunk size 272144
config.namingFunction {Function} Isomorphic Function which returns String false Primary sets file name on FS
if namingFunction is not set
FS-name is equal to file's record _id
config.permissions {Number} Server FS-permissions (access rights) in octal 0644 ex.: 0755, 0777
config.parentDirPermissions {Number} Server FS-permissions for parent directory (access rights) in octal 0755 ex.: 0777
config.integrityCheck {Boolean} Server CRC file check true
config.strict {Boolean} Server Strict mode for partial content false If is true server will return 416 response code, when range is not specified
config.downloadCallback {Function} Server Called right before initiate file download.
Arguments:
Context:
  • this.request
  • this.response
  • this.user()
  • this.userId
false Function should return {Boolean} value, return false to abort download, and true to continue
config.protected {Boolean|Function} Server Control download flow.
If function:
Arguments:
  • fileObj {Object|null} - If requested file exists - file object, otherwise - null

Context:
  • this.request
  • this.response
  • this.user()
  • this.userId
false If true - files will be served only to authorized users, if function() - you're able to check visitor's permissions in your own way.
  • return true to continue
  • return false to abort or {Number} to abort upload with specific response code, default response code is 401
config.public {Boolean} Isomorphic Allows to place files in public directory of your web-server. And let your web-server to serve uploaded files false Important notes:
  • Collection can not be public and protected at the same time!
  • downloadRoute must be explicitly provided. And pointed to root of web/proxy-server, like /uploads/
  • storagePath must point to absolute root path of web/proxy-server, like '/var/www/myapp/public/uploads/'
  • integrityCheck is not guaranteed!
  • play and force download features is not guaranteed!
  • Remember: NodeJS is not best solution for serving files
config.onBeforeUpload {Function} Isomorphic Callback, triggered right before upload is started on client and right after receiving a chunk on server
Arguments:
  • fileData {Object} - Current file metadata

Context:
  • this.file
  • this.user()
  • this.userId
  • this.chunkId {Number} - On server only
  • this.eof {Boolean} - On server only
false
  • return true to continue
  • return false to abort or {String} to abort upload with message

note: Because sending meta data as part of every chunk would hit the performance, meta is always empty ({}) except on the first chunk (chunkId=1 or chunkId=-1) and on eof (eof=true or chunkId=-1) (Fixed. Since v1.6.0 full file object is available in onBeforeUpload callback)

config.onInitiateUpload {Function} Server Function which executes on server right before upload is begin and right after onBeforeUpload hook returns true. This hook called only once per upload and fully asynchronous.
Arguments:
  • fileData {Object} - Current file metadata

Context:
  • this.file
  • this.user()
  • this.userId
  • this.chunkId {Number} - On server only
false See: #208
config.onBeforeRemove {Function} Server Callback, triggered right before remove file (from Client)
Arguments:
  • cursor {MongoCursor} - Current files to be removed on cursor, if has any

Context:
  • this.user()
  • this.userId
false Use with allowClientCode to control access to remove() method.
  • return true to continue
  • return false to abort
config.onAfterUpload {Function} Server Callback, triggered after file is written to FS
Arguments:
  • fileRef {Object} - Record from MongoDB
false Alternatively use: addListener('afterUpload', func)
config.onAfterRemove {Function} Server Callback, triggered after file(s) is removed from Collection
Arguments:
  • files {[Object]} - Array of removed documents
false
config.onbeforeunloadMessage {String|Function} Client Message shown to user when closing browser's window or tab, while upload in the progress Upload in a progress... Do you want to abort?
config.allowClientCode {Boolean} Isomorphic Allow use remove() method on client true
config.debug {Boolean} Isomorphic Turn on/of debugging and extra logging false
config.interceptDownload {Function} Server Intercept download request.
Arguments:
  • http {Object} - Middleware request instance
  • http.request {Object} - example: http.request.headers
  • http.response {Object} - example: http.response.end()
  • fileRef {Object} - Current file record from MongoDB
  • version {String} - Requested file version
false Usage example: Serve file from third-party resource.
  • return false from this function to continue standard behavior
  • return true to intercept incoming request
config.disableUpload {Boolean} Both Disable upload from Client to Server (HTTP and DDP (WebSockets)) false Use for security reasons when only Server usage is needed
config.disableDownload {Boolean} Server Disable file download from Server to Client (HTTP) false Use for security reasons when only upload from Client to Server usage is needed, and files shouldn't be downloaded by any user.
config._preCollection {Mongo.Collection} Server Mongo.Collection Instance You can pass your own Mongo Collection instance {_preCollection: new Mongo.Collection('__pre_myFiles')}
config._preCollectionName {String} Server preCollection name __pre_MeteorUploadFiles

Event map:

Name Locus Description Comment
afterUpload Isomorphic Triggered right after file is written to FS.
Arguments:
  • fileRef {Object} - Record from MongoDB

Examples:

import { FilesCollection } from 'meteor/ostrio:files';
const Images = new FilesCollection({
  storagePath: 'assets/app/uploads/Images',
  downloadRoute: '/files/images',
  collectionName: 'Images',
  permissions: 0o755,
  allowClientCode: false,
  cacheControl: 'public, max-age=31536000',
  // Read more about cacheControl: https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers
  onbeforeunloadMessage() {
    return 'Upload is still in progress! Upload will be aborted if you leave this page!';
  },
  onBeforeUpload(file) {
    // Allow upload files under 10MB, and only in png/jpg/jpeg formats
    // Note: You should never trust to extension and mime-type here
    // as this data comes from client and can be easily substitute
    // to check file's "magic-numbers" use `mmmagic` or `file-type` package
    // real extension and mime-type can be checked on client (untrusted side)
    // and on server at `onAfterUpload` hook (trusted side)
    if (file.size <= 10485760 && /png|jpe?g/i.test(file.ext)) {
      return true;
    }
    return 'Please upload image, with size equal or less than 10MB';
  },
  downloadCallback(fileObj) {
    if (this.params.query.download == 'true') {
      // Increment downloads counter
      Images.update(fileObj._id, {$inc: {'meta.downloads': 1}});
    }
    // Must return true to continue download
    return true;
  },
  protected(fileObj) {
    // Check if current user is owner of the file
    if (fileObj.meta.owner === this.userId) {
      return true;
    }
    return false;
  }
});

// Export FilesCollection instance, so it can be imported in other files
export default Images;

Add extra security:

Attach schema [Isomorphic]:

To attach schema, use/install aldeed:collection2 and simple-schema packages.

import { FilesCollection } from 'meteor/ostrio:files';
const Images = new FilesCollection({/* ... */});
Images.collection.attachSchema(new SimpleSchema(Images.schema));

You're free to extend the schema to include your own properties. The default schema is stored under FilesCollection.schema object.

import { FilesCollection } from 'meteor/ostrio:files';
const mySchema = {
  ...FilesCollection.schema,
  myProp: String,
  myOtherProp: {
    type: Array
  }
};
const Images = new FilesCollection({
  /* ... */
  schema: mySchema
});
Images.collection.attachSchema(new SimpleSchema(mySchema));

Deny collection interaction on client [Server]:

Deny insert/update/remove from client

import { Meteor }          from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

if (Meteor.isServer) {
  const Images = new FilesCollection({/* ... */});
  Images.deny({
    insert() {
      return true;
    },
    update() {
      return true;
    },
    remove() {
      return true;
    }
  });

  /* Equal shortcut: */
  Images.denyClient();
}

Allow collection interaction on client [Server]:

Allow insert/update/remove from client

import { Meteor }          from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

if (Meteor.isServer) {
  const Images = new FilesCollection({/* ... */});
  Images.allow({
    insert() {
      return true;
    },
    update() {
      return true;
    },
    remove() {
      return true;
    }
  });

  /* Equal shortcut: */
  Images.allowClient();
}

Events listeners:

import { FilesCollection } from 'meteor/ostrio:files';
const Images = new FilesCollection({/* ... */});
// Alias addListener
Images.on('afterUpload', function (fileRef) {
  /* `this` context is the Images (FilesCollection) instance */
});

Use onBeforeUpload to avoid unauthorized upload:

import { FilesCollection } from 'meteor/ostrio:files';
const Images = new FilesCollection({
  collectionName: 'Images',
  allowClientCode: true,
  onBeforeUpload() {
    if (this.userId) {
      const user = this.user();
      if (user.profile.role === 'admin') {
        // Allow upload only if
        // current user is signed-in
        // and has role is `admin`
        return true;
      }
    }

    return 'Not enough rights to upload a file!';
  }
});

Use onBeforeRemove to avoid unauthorized remove:

For more info see remove method.

import { FilesCollection } from 'meteor/ostrio:files';
const Images = new FilesCollection({
  collectionName: 'Images',
  allowClientCode: true,
  onBeforeRemove() {
    if (this.userId) {
      const user = this.user();
      if (user.profile.role === 'admin') {
        // Allow removal only if
        // current user is signed-in
        // and has role is `admin`
        return true;
      }
    }

    return false;
  }
});

Use onAfterUpload to avoid mime-type and/or extension substitution:

For additional security, it's recommended to verify the mimetype by looking at the content of the file and delete it, if it looks malicious. E.g. you can use mmmagic package for this:

import { Meteor }          from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

const Images = new FilesCollection({
  collectionName: 'Images',
  onAfterUpload(file) {
    if (Meteor.isServer) {
      // check real mimetype
      const { Magic, MAGIC_MIME_TYPE } = require('mmmagic');
      const magic = new Magic(MAGIC_MIME_TYPE);
      magic.detectFile(file.path, Meteor.bindEnvironment((err, mimeType) => {
        if (err || !~mimeType.indexOf('image')) {
          // is not a real image --> delete
          console.log('onAfterUpload, not an image: ', file.path);
          console.log('deleted', file.path);
          this.remove(file._id);
        }
      }));
    }
  }
});
You can’t perform that action at this time.