Meteor-Files is a free, open source file management & sync project written in JavaScript and released under BSD-3-Clause. It has 1,116 GitHub stars, 165 forks and 19 open issues, and was last pushed 16 days ago. On this registry it ranks #37 of 39 tracked projects in File Management & Sync, with 5 head-to-head comparisons available.

What is Meteor-Files?

Meteor-Files is an open-source Meteor.js package, published as ostrio:files under the BSD-3-Clause licence, that adds file upload and file management to Meteor applications using the MongoDB Collection API, and it is aimed at JavaScript developers building on Meteor.js who need uploads without adopting a separate file-handling API.

What it is

Meteor-Files is a Meteor.js package written in JavaScript and released under the BSD-3-Clause licence. Its maintainers describe it as a stable, fast, robust and well-maintained package for file management that uses the MongoDB Collection API, and it supports uploads to AWS S3, GridFS, Google Storage, Dropbox and other third-party storage. The package is hackable through hooks and events.

The problem it solves is the file-upload gap in Meteor applications. Without a package of this kind, a team on Meteor.js has to wire up and learn a separate file-handling API next to the MongoDB Collection API the rest of the application already uses. Meteor-Files replaces that second API with operations such as .insertAsync(), which initiates an upload and inserts the MongoDB record when the transfer completes, and .removeAsync(), which erases the stored file and its record. It also decouples the binary payload from the application server, so files can live in GridFS, AWS S3, Google Storage or Dropbox rather than on a local filesystem β€” which matters on computing clouds that have no persistent file system, such as Heroku.

Key capabilities

  • .insertAsync() starts an upload and inserts a record into a MongoDB collection; .removeAsync() erases the stored file and its record.
  • Uploads travel over two transports, HTTP and DDP, with documentation explaining the difference between them.
  • Uploads are sustainable and "resumable", auto-resuming when the connection is interrupted or the server is rebooted (not supported on Heroku and similar platforms).
  • Storage backends include GridFS, AWS S3, Google Storage and Dropbox, alongside other third-party storage, each with its own integration documentation.
  • The onBeforeUpload hook exposes a file's mime-type, size and extension for validation before an upload is accepted.
  • The onAfterUpload and onAfterRemove hooks support post-processing such as image resizing and subversions management.
  • Compatible with all front-end frameworks from Blaze to React.

Who uses it and how

  • Meteor.js teams deploying to computing clouds without a persistent file system, such as Heroku, pushing file payloads to an external store while MongoDB holds the metadata; resumable uploads are explicitly unsupported there.
  • Applications that keep binaries in AWS S3, Google Storage, GridFS or Dropbox while file records sit in the same MongoDB collection as the rest of the application's data.
  • Projects needing server-side image work, using onAfterUpload to resize images and manage subversions rather than running a separate processing pipeline.
  • Teams working across view layers, since compatibility runs from Blaze to React and does not lock an application into one front-end framework.

Getting started

Install ostrio:files from Atmosphere, following the installation section of the README. The package page at packosphere.com/ostrio/files and the repository's documentation table of contents carry the API reference, usage example, FAQ and demo applications.

How it compares

The facts provided here name no comparable or paid products, so no head-to-head comparison is possible and Meteor-Files stands alone in this registry. Within its own ecosystem, it is delivered as a Meteor package built on the MongoDB Collection API rather than as a separate service.

When to use it β€” and when not to

Adopting Meteor-Files means operating a Meteor server and a MongoDB database, plus configuring credentials for AWS S3, Google Storage or Dropbox when files are not kept in GridFS or on the local filesystem. Applications that are not built on Meteor.js should not choose it, because the package's value comes from a MongoDB Collection API the surrounding application already uses. Self-hosters on Heroku and comparable platforms must also accept that resumable uploads are not supported there, which is the one clear functional limitation documented in the README.

project readme (upstream, from github) β€” read inline

support support Mentioned in Awesome ostrio:files GitHub stars ostr.io meteor-files.com

Files for Meteor.js

Stable, fast, robust, and well-maintained Meteor.js package for files management using MongoDB Collection API. Call .insertAsync() method to initiate a file upload and to insert new record into MongoDB collection after upload is complete. Calling .removeAsync() method would erase stored file and record from MongoDB Collection. And so on, no need to learn new APIs. Hackable via hooks and events. Supports uploads to AWS:S3, GridFS, Google Storage, DropBox, and other 3rd party storage.

ToC:

Key features

Installation:

Install ostrio:files from Atmosphere

meteor add ostrio:files

ES6 Import:

Import in isomorphic location (e.g. on server and client)

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

API overview

For detailed docs, examples, and API β€” read documentation section.

Main methods:

Constructor

[Anywhere]. Initiate file's collection in the similar way to Mongo.Collection with optional settings related to file-uploads. Read full docs for FilesCollection Constructor in the API documentation.

import { FilesCollection } from 'meteor/ostrio:files';
new FilesCollection(FilesCollectionConfig);

Pass additional options to control upload-flow

// shared: /imports/lib/collections/images.collection.js
import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

const imagesCollection = new FilesCollection({
  collectionName: 'images',
  allowClientCode: false, // Disallow remove files from Client
  onBeforeUpload(file) {
    // Allow upload files under 10MB, and only in png/jpg/jpeg formats
    if (file.size <= 10485760 && /png|jpg|jpeg/i.test(file.extension)) {
      return true;
    }
    return 'Please upload image, with size equal or less than 10MB';
  }
});

if (Meteor.isClient) {
  // SUBSCRIBE TO ALL UPLOADED FILES ON THE CLIENT
  Meteor.subscribe('files.images.all');
}

if (Meteor.isServer) {
  // PUBLISH ALL UPLOADED FILES ON THE SERVER
  Meteor.publish('files.images.all', function () {
    return imagesCollection.collection.find();
  });
}

Upload a file

import { FilesCollection } from 'meteor/ostrio:files';
const files = new FilesCollection(FilesCollectionConfig);
files.insertAsync(config: InsertOptions, autoStart?: boolean): Promise<FileUpload | UploadInstance>;

Read full docs for insertAsync() method

Upload form (template):

<template name="uploadForm">
  {{#with currentUpload}}
    Uploading <b>{{file.name}}</b>:
    <span id="progress">{{progress.get}}%</span>
  {{else}}
    <input id="fileInput" type="file" />
  {{/with}}
</template>

Shared code:

import { FilesCollection } from 'meteor/ostrio:files';
const imagesCollection = new FilesCollection({collectionName: 'images'});
export default imagesCollection; // import in other files

Client's code:

import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
Template.uploadForm.onCreated(function () {
  this.currentUpload = new ReactiveVar(false);
});

Template.uploadForm.helpers({
  currentUpload() {
    return Template.instance().currentUpload.get();
  }
});

Template.uploadForm.events({
  async 'change #fileInput'(e, template) {
    if (e.currentTarget.files && e.currentTarget.files[0]) {
      // We upload only one file, in case
      // multiple files were selected
      const upload = await imagesCollection.insertAsync({
        file: e.currentTarget.files[0],
        chunkSize: 'dynamic'
      }, false);

      upload.on('start', function () {
        template.currentUpload.set(this);
      });

      upload.on('end', function (error, fileObj) {
        if (error) {
          alert(`Error during upload: ${error}`);
        } else {
          alert(`File "${fileObj.name}" successfully uploaded`);
        }
        template.currentUpload.set(false);
      });

      await upload.start();
    }
  }
});

For multiple file upload see this demo code.

Upload base64 string (introduced in v1.7.1):

// As dataURI
await imagesCollection.insertAsync({
  file: 'data:image/png,base64str…',
  isBase64: true, // <β€” Mandatory
  fileName: 'pic.png' // <β€” Mandatory
});

// As plain base64:
await imagesCollection.insertAsync({
  file: 'base64str…',
  isBase64: true, // <β€” Mandatory
  fileName: 'pic.png', // <β€” Mandatory
  type: 'image/png' // <β€” Mandatory
});

For more expressive example see Upload demo app

Stream files

To display files you can use fileURL template helper or link() method of FileCursor instance.

Template:

<template name='file'>
  <img src="{{imageFile.link}}" alt="{{imageFile.name}}" />
  <!-- Same as: -->
  <!-- <img src="{{fileURL imageFile}}" alt="{{imageFile.name}}" /> -->
  <hr>
  <video height="auto" controls="controls">
    <source src="{{videoFile.link}}?play=true" type="{{videoFile.type}}" />
    <!-- Same as: -->
    <!-- <source src="{{fileURL videoFile}}?play=true" type="{{videoFile.type}}" /> -->
  </video>
</template>

Shared code:

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

const imagesCollection = new FilesCollection({ collectionName: 'images' });
const videosCollection = new FilesCollection({ collectionName: 'videos' });

if (Meteor.isServer) {
  // Upload sample files on server's startup:
  Meteor.startup(async () => {
    await imagesCollection.loadAsync('https://raw.githubusercontent.com/veliovgroup/Meteor-Files/master/logo.png', {
      fileName: 'logo.png'
    });
    await videosCollection.loadAsync('http://www.sample-videos.com/video/mp4/240/big_buck_bunny_240p_5mb.mp4', {
      fileName: 'Big-Buck-Bunny.mp4'
    });
  });

  Meteor.publish('files.images.all', function () {
    return imagesCollection.collection.find();
  });

  Meteor.publish('files.videos.all', function () {
    return videosCollection.collection.find();
  });
} else {
  // Subscribe to file's collections on Client
  Meteor.subscribe('files.images.all');
  Meteor.subscribe('files.videos.all');
}

Client's code:

// imports/client/file/file.js
import '/imports/client/file/file.html';
import imagesCollection from '/imports/lib/collections/images.collection.js';

Template.file.helpers({
  imageFile() {
    return imagesCollection.findOne();
  },
  videoFile() {
    return videosCollection.findOne();
  }
});

For more expressive example see Streaming demo app

Download button

Create collection available to Client and Server

// imports/lib/collections/images.collection.js
import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';
const imagesCollection = new FilesCollection({ collectionName: 'images' });

if (Meteor.isServer) {
  // Load sample image into FilesCollection on server's startup:
  Meteor.startup(async () => {
    await imagesCollection.loadAsync('https://raw.githubusercontent.com/veliovgroup/Meteor-Files/master/logo.png', {
      fileName: 'logo.png',
    });
  });

  Meteor.publish('files.images.all', function () {
    return imagesCollection.collection.find();
  });
} else {
  // Subscribe on the client
  Meteor.subscribe('files.images.all');
}

Create template, call .link method on the FileCursor returned from file helper

<!-- imports/client/file/file.html -->
<template name='file'>
  <a href="{{file.link}}?download=true" download="{{file.name}}" target="_parent">
    {{file.name}}
  </a>
</template>

Create controller for file template with file helper that returns FileCursor with .link() method

// imports/client/file/file.js
import '/imports/client/file/file.html';
import imagesCollection from '/imports/lib/collections/images.collection.js';

Template.file.helpers({
  file() {
    return imagesCollection.findOne();
  }
});

For more expressive example see Download demo

FAQ:

  1. Where are files stored by default?: by default if config.storagePath isn't set in Constructor options it's equals to assets/app/uploads and relative to running script:
    • a. On development stage: yourDevAppDir/.meteor/local/build/programs/server. Note: All files will be removed as soon as your application rebuilds or you run meteor reset. To keep your storage persistent during development use an absolute path outside of your project folder, e.g. /data directory.
    • b. On production: yourProdAppDir/programs/server. Note: If using MeteorUp (MUP), Docker volumes must to be added to mup.json, see MUP usage
  2. Cordova usage and development: With support of community we do regular testing on virtual and real devices. To make sure Meteor-Files library runs smoothly in Cordova environment β€” enable withCredentials; enable {allowQueryStringCookies: true} and {allowedOrigins: true} on both Client and Server. For more details read Cookie's repository FAQ
  3. meteor-desktop usage and development: Meteor-Files can be used in meteor-desktop projects as well. As meteor-desktop works exactly like Cordova, all Cordova requirements and recommendations apply
  4. How to pause/continue upload and get progress/speed/remaining time?: see FileUpload instance returned from insertAsync method
  5. When using any of accounts packages - package accounts-base must be explicitly added to .meteor/packages above ostrio:files
  6. cURL/POST uploads - Take a look on POST-Example by @noris666
  7. In Safari (Mobile and Desktop) for DDP chunk size is reduced by algorithm, due to error thrown if frame is too big. This issue should be fixed in Safari 11. Switching to http transport (which has no such issue) is recommended for Safari. See #458
  8. Make sure you're using single domain for the Meteor app, and the same domain for hosting Meteor-Files endpoints, see #737 for details
  9. When requests are proxied to FilesCollection endpoint make sure protocol http/1.1 is used, see #742 for details

Awards:

GCAA award

Get Support:

Demo applications:

Fully-featured file-sharing app:

Other demos:

Related Packages:

Support Meteor-Files project:

Contribution:

  • Want to help? Please check issues for open and tagged as help wanted issues;
  • Want to contribute? Read and follow PR rules. All PRs are welcome on dev branch. Please, always give expressive description to your changes and additions.

Supporters:

We would like to thank everyone who support this project

Frequently asked questions

Is Meteor-Files free to use?

Meteor-Files is open source under the BSD-3-Clause licence. There is no licence fee and no seat count β€” you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does Meteor-Files do?

πŸš€ Upload files via DDP or HTTP to β˜„οΈ Meteor server FS, AWS, GridFS, DropBox or Google Drive. Fast, secure and robust.

What is Meteor-Files written in?

Meteor-Files is primarily written in JavaScript. Its source is publicly available at https://github.com/veliovgroup/Meteor-Files, and it has 1,116 GitHub stars.