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.
- π Documentation - Docs, API, Demos, Examples
- β¨ Key features
- π API Documentation
- β‘οΈ Quick start:
- π€ FAQ
- π Awards
- πββοΈ Get Help
- ποΈ Demo applications
- π¦ Related Packages
- π Support this project
- π¨βπ» Contribute to this project
- π Supporters
- Compatible with all front-end frameworks from Blaze to React
- Upload via
HTTP
andDDP
transports, read about difference - Sustainable and "resumable" uploads will auto-resume when connection interrupted or server rebooted
- Upload files through computing cloud without persistent File System, like Heroku ("resumable" uploads are not supported on Heroku and alike)
- Use GridFS, AWS S3, Google Storage or DropBox and other (3rd-party storage)
- APIs for checking file mime-type, size, extension, an other file's properties before upload using
onBeforeUpload
hook - APIs for resizing images, subversions management, and other post-processing tasks using
onAfterUpload
andonAfterRemove
hooks
Install ostrio:files
from Atmosphere
meteor add ostrio:files
Import in isomorphic location (e.g. on server and client)
import { FilesCollection } from 'meteor/ostrio:files';
For detailed docs, examples, and API β read documentation section.
Main methods:
FilesCollection
Constructor [Anywhere] - Initialize FilesCollectioninsertAsync()
[Client] - Upload a file to server, returnsFileUpload
instancelink()
[Anywhere] - Generate downloadable linkfind()
[Anywhere] - Find all files matching selector, returnsFilesCursor
instancefindOneAsync()
[Anywhere] - Find a single file record matching selector, returnsFileCursor
instanceremoveAsync()
[Anywhere] - Asynchronously remove files from FilesCollection and "unlink" (e.g. remove) from ServeraddFile()
[Server] - Add local file to FilesCollection from FSloadAsync()
[Server] - Write file to FS and FilesCollection from remote URLwriteAsync()
[Server] - WriteBuffer
to FS and FilesCollection
[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();
});
}
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
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
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
- Where are files stored by default?: by default if
config.storagePath
isn't set in Constructor options it's equals toassets/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 runmeteor 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 tomup.json
, see MUP usage
- a. On
- 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 bothClient
andServer
. For more details read Cookie's repository FAQ - 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
- How to pause/continue upload and get progress/speed/remaining time?: see FileUpload instance returned from
insertAsync
method - When using any of
accounts
packages - packageaccounts-base
must be explicitly added to.meteor/packages
aboveostrio:files
- cURL/POST uploads - Take a look on POST-Example by @noris666
- 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 tohttp
transport (which has no such issue) is recommended for Safari. See #458 - Make sure you're using single domain for the Meteor app, and the same domain for hosting Meteor-Files endpoints, see #737 for details
- When requests are proxied to
FilesCollection
endpoint make sure protocolhttp/1.1
is used, see #742 for details

- Ask a question or submit an issue
- Releases / Changelog / History
- For more docs and examples read wiki
Fully-featured file-sharing app:
Other demos:
- pyfiles (meteor-python-files) Python Client for Meteor-Files package
- meteor-autoform-file - Upload and manage files with autoForm
- ποΈ Upload and share files using meteor-files.com β Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination
- π¨βπ» Improve your project using ostr.io for Server Monitoring, Web Analytics, WebSec, Web-CRON and SEO Pre-rendering
- π΅ Sponsor via GitHub
- π΅ Support via PayPal
- βοΈ Star on GitHub
- βοΈ Star on Atmosphere
- 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.
We would like to thank everyone who support this project
- @vanshady
- @Neophen
- @rikyperdana
- @derwok, check out his project - 4minitz
- @FinnFrotscher
- @Neobii
- @themeteorchef
- @MeDBejoHok
- @martunta