Meteor-Files: a file upload package that keeps the MongoDB collection API
🚀 Upload files via DDP or HTTP to ☄️ Meteor server FS, AWS, GridFS, DropBox or Google Drive. Fast, secure and robust.
At a glance
- What is it?
- ostrio:files is a Meteor package that stores uploaded files behind a MongoDB collection, so the API looks like a collection API: insertAsync to upload and removeAsync to erase. The interesting engineering is in the transport and the storage layers, with HTTP and DDP uploads, resumable transfers, and GridFS, S3, Google Storage and Dropbox as interchangeable backends.
- Who is it for?
- Meteor-Files fits a Meteor application that needs file uploads with resumable transfers and a choice of storage backend, and that wants its upload code to look like the rest of its data access.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The design: a collection API, not a file API
Meteor-Files is described as a stable, fast, robust and well maintained Meteor.js package for files management using the MongoDB Collection API, and that phrase is the design statement.
Call .insertAsync() to initiate a file upload and to insert a new record into a MongoDB collection after the upload is complete. Call .removeAsync() to erase the stored file and the record from the MongoDB collection. And so on, with no need to learn new APIs, and hackable via hooks and events.
That last point is why a Meteor package would be worth a separate look in a world of standalone upload libraries. A file record is a document in a collection, so a query for a file is a query, a publication of files is a publication, and a reactive UI that subscribes to a cursor updates itself. The storage is decoupled from the metadata, and the metadata lives where the rest of your data lives.
The install is the standard Meteor package add:
meteor add ostrio:filesAnd the import is isomorphic, on server and client, from the meteor namespace:
import { FilesCollection } from 'meteor/ostrio:files';The requirement is stated plainly: Meteor 3.2 or newer, which is version 3.1.0 and later. That is a recent floor, and it is a consequence of the v3 rewrite rather than an arbitrary choice.
The primary language is JavaScript, the licence is BSD-3-Clause, and the homepage is packosphere.com/ostrio/files. The releases are v3.1.0 on 2026-10-01, v3.0.1 on 2025-09-24 and v3.0.0 on 2025-09-24, so the 3.0 major landed in September 2025 and the current patch line is being maintained. The repository is not archived with the last push on 2026-10-01.
The API surface is documented per method, and the tags used in the list are informative. FilesCollection Constructor is Isomorphic, and it initiates the file collection in a similar way to Mongo.Collection with optional settings related to file upload. insertAsync() is Client only, and it uploads a file to the server, returning a Promise that resolves with the UploadInstance, the FileUpload, when autoStart is true. link() is Isomorphic and generates a downloadable link. find() and findOneAsync() are Isomorphic and return FilesCursor and FileCursor instances respectively. removeAsync() is Isomorphic and asynchronously removes files from the collection and unlinks them from the server. addFile() is Server only and adds a local file from the filesystem. loadAsync() is Server only and writes a file to the filesystem and the collection from a remote URL. writeAsync() is Server only and writes a Buffer to the filesystem and the collection.
That Isomorphic and Server and Client labelling is the useful part of the list, because it tells you at a glance which calls are safe to put in shared code and which must be kept on one side.
Transports, and the resumable upload story
There are two ways to move the bytes, and the project documents the difference in a page of its own.
Upload via HTTP and DDP transports. DDP is Meteor's own real time protocol, so a DDP upload rides the connection the application already has open rather than opening a second one, and HTTP is the alternative for cases where a normal request is the better fit. The feature list links a document for the difference.
The property that matters more than the choice of transport is the second one. Sustainable and resumable uploads will auto-resume when the connection is interrupted or the server rebooted. That is a considerably stronger guarantee than a failed upload, because the state of a partially transferred file is persisted, and the transfer picks up where it stopped.
The following bullet states the limit of that guarantee, and it is stated in the documentation rather than discovered in production. Upload files through computing cloud without a persistent File System, like Heroku, and resumable uploads are not supported on Heroku and alike.
The reason follows from how resumability is implemented. Resuming an upload requires somewhere to record how much of the file has been received, and on a platform where the local disk is ephemeral that record disappears with the container, so the resume point is lost along with it. So the feature set has one coherent boundary: a platform with a persistent filesystem gets resumable uploads, and one without does not.
That has a direct consequence for deployment planning. If you are deploying to a container platform with an ephemeral disk, the resumability claim does not apply to you, and you should treat upload reliability accordingly rather than assuming the headline feature is available.
The compatibility with front end frameworks is the other item worth reading. Compatible with all front-end frameworks from Blaze to React, and there is a separate React example document linked. For a package with a Meteor specific API, that matters more than it would for a framework agnostic library, because the front end is where the upload call lives.
Storage backends, and why the choice is not just about where bytes land
The package treats storage as a swappable backend, and the list of supported ones is long: GridFS, AWS S3, Google Storage, DropBox, and other third party storage providers, each with its own integration document.
GridFS comes first in the list, and that is a deliberate default rather than an accident. GridFS is MongoDB's own file storage, storing files as chunks in a collection alongside the metadata. For a package whose whole premise is that file records live in a MongoDB collection, keeping the bytes in the same database means one backup, one transaction boundary, and no second system to keep consistent.
The cloud backends solve a different problem. If your deployment is on a platform with an ephemeral disk, or if you want the files served from a CDN, then the bytes need to be somewhere durable and geographically distributed, and the metadata stays in MongoDB. The package supports AWS S3, Google Storage and DropBox, and a general third party storage document for others.
Two of the integration documents have titles that reveal implementation detail. The GridFS one is a bucket integration document, which suggests that a GridFSBucket can be used as the storage, and the DropBox one is a separate document rather than being folded into the generic third party page, which suggests the Dropbox integration needed its own handling, likely around how the service presents download links.
That last point is a real consideration the API design accounts for. The link() method generates a downloadable link, and the storage backend is where the link leads. So the link generation is backend aware, which is the part that a naive implementation of a pluggable storage layer usually gets wrong.
The feature list also mentions other third party storage generally, and the topic tags name aws-s3, google-storage, dropbox and gridfs individually, which is consistent with each having a first class integration rather than being a generic escape hatch.
The hooks: validation, image resizing and subversions
The post-processing surface is what turns an upload handler into a pipeline, and it is built on two hooks.
There are APIs for checking file mime-type, size, extension and other file properties before upload, using the onBeforeUpload hook. The onBeforeUpload hook runs before the bytes are accepted, so it is the place to enforce what your application allows, and the file properties it can inspect are the ones an attacker would lie about if given the chance.
Then there are APIs for resizing images, subversions management and other post-processing tasks, using the onAfterUpload and onAfterRemove hooks. So the sequence is onBeforeUpload to validate, the upload itself, onAfterUpload to post-process, and onAfterRemove to clean up whatever the post-processing produced.
The image resizing is a concrete, useful example, and the documentation links the actual server-side code for it. The subversions management feature is more interesting and less common. It is the idea that a file can have multiple versions, and that the set of versions is itself manageable, which is what you need when a document is revised and you want to keep the history without a separate records system.
That is a natural fit with the collection model, because a version is another document in a collection, and the reactive query that shows a file can show its versions.
The onAfterRemove hook is the piece that closes the loop. If onAfterUpload wrote derived data, a thumbnail, a resized variant, an index entry, then removing the file has to remove those too, and an onAfterRemove hook is how that happens. A package that offers the processing hook without the cleanup hook would leave orphans; offering both is the sign of a design that has been used in production for a while.
The documentation also includes a security guide as a separate document in the table of contents, which for an upload package is the right thing to have, since an upload endpoint is one of the more exposed surfaces in a web application. The README does not summarise what is in it, so it is worth reading directly rather than inferring the guidance from the feature list.
The repository layout, and what the build tooling says
The top level listing tells you how the package is put together, and it is a hybrid that reflects its history.
There are the Meteor package files, package.js,.npmignore,.meteorignore and.versions, and the entry points for each side of the isomorphism: client.js, server.js, core.js, lib.js, upload.js, write-stream.js, cursor.js and index.d.ts for the types. The worker.js and worker.min.js are the upload worker, and a worker script is how the file bytes actually move, which is a different mechanism from the collection API that describes them.
The docs/ directory is large and structured, with a separate document per API method, a toc.md, a readme.md for the API documentation, and dedicated pages for the constructor, insertAsync, removeAsync, link, find, findOneAsync, addFile, loadAsync and writeAsync. There are also pages for the security guide, the migration to v3, the constructor, the transports, the GridFS bucket integration, the AWS S3 integration, the Google Cloud Storage integration, the DropBox integration, the third party storage page, the file subversions page, the React example, and the image processing example hosted in a separate website repository.
That documentation structure tells you the project's priorities. Every method has its own page, which is what a package with a wide API surface needs, and the migration to v3 guide is in the table of contents at the top level, which is the right place for it given that v3.0 changed the API.
The build and test tooling is the interesting part. The package.json is marked private, and the name is ostrio-meteor-files with the version 3.1.0, which is the npm-side name rather than the Atmosphere name.
The test setup uses Meteor's own package test runner with the mocha driver, as a once run and as a watch run, so the suite runs inside the Meteor environment rather than as a standalone node test. Linting is eslint with a flat config in eslint.config.mjs. Type checking is tsc --noEmit plus tsd, and the tsd configuration points at index.test-d.ts, which means the type level tests are separate files that assert the public type signatures.
The dev dependencies give a fair picture of the runtime surface. There is mongodb, which is the driver for the collection, and simpl-schema for the schema validation of the file records. chai and sinon are the assertion and mocking libraries. eventemitter3 is the event emitter, which is what the hooks and events in the README description are built on. typescript at roughly 5.9, eslint at 9, and the type packages for connect, meteor and node.
The demo/ directory has a README and several applications, and the names visible in the listing are demo-simplest-upload, demo-simplest-download-button, demo-simplest-streaming and demo/ suggest the demos are deliberately minimal, one feature each, which is the right way to demonstrate a library whose selling point is that its API mirrors something people already know.
Editorial conclusion
Meteor-Files fits a Meteor application that needs file uploads with resumable transfers and a choice of storage backend, and that wants its upload code to look like the rest of its data access. It is the wrong tool if your application is not on Meteor, since the package is a Meteor package with a Meteor specific collection API, and the wrong choice if you need to run on a platform without a persistent filesystem, since the documentation is explicit that resumable uploads are not supported on Heroku and alike. Before adopting, read the security guide, and check the migration to v3 document, because the major version changed the API and there is a dedicated guide for it.
Frequently asked questions
How do I install and use Meteor-Files?
Run meteor add ostrio:files, which requires Meteor 3.2 or newer, and import the collection in an isomorphic location with import { FilesCollection } from 'meteor/ostrio:files'. Then call .insertAsync() to upload a file and .removeAsync() to erase one, and the file record is a document in a MongoDB collection like any other.
What storage backends does Meteor-Files support?
GridFS, AWS S3, Google Storage, DropBox and other third party storage providers, each with its own integration document. GridFS keeps the bytes in MongoDB alongside the metadata, while the cloud backends serve the files from durable storage while the record stays in the collection.
Are uploads resumable in Meteor-Files?
Yes, sustainable and resumable uploads auto-resume when the connection is interrupted or the server rebooted. The exception is platforms without a persistent filesystem: the documentation states that resumable uploads are not supported on Heroku and alike, because the state needed to resume cannot be kept on an ephemeral disk.
What hooks does Meteor-Files provide?
onBeforeUpload for checking file mime-type, size, extension and other properties before the upload, and onAfterUpload and onAfterRemove for post-processing such as resizing images and managing subversions, and for cleaning up what that processing produced when a file is removed.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/veliovgroup-meteor-files)Was this useful?