Mountable File Server can be used with any Ruby (on Rails) application and it removes the pain associated with file uploads.
app only stores reference to uploaded file
The core of Mountable File Server (MFS) is a small HTTP API that accepts file uploads and offers endpoints to interact with uploaded files. While the frontend deals directly with the HTTP API, your Ruby application will want to use the Ruby adapter MountableFileServer::Adapter
.
MountableFileServer was born out of my frustations with existing Ruby based file upload solutions. It is certainly not comparable with existing feature-rich plug-and-play solutions that are tied to Ruby on Rails.
The fundamental idea is that your application should not be tied to files or handling the upload process. Instead this is handled by a dedicated server which can be run as independet process or be mounted inside your application. It accepts file uploads and returns an unique identifier for the file. This unique identifier is basically a string and the only thing your application has to remember in order to work with uploaded files.
- JavaScript function to upload files via AJAX.
- JavaScript events regarding the uploads various states.
- Uploaded files are stored on disk.
- Filenames are unique and random sequences of charaters.
- Uploaded files are stored temporary until they are explicitly moved to permanent storage by the application.
- Uploaded files can be of public or private nature.
- Private files can't be accessed via an URL.
Things it does not do:
- Image processing. Processing images has nothing to do with uploading files. MountableImageProcessor is recommended for adding on the fly image processing to your application.
Add the MountableFileServer gem to your Gemfile and bundle install
. Make sure you include vendor/assets/javascripts/mountable_file_server.js
in your frontend code.
gem 'mountable_file_server', '~> 0.0.2'
If your application is built upon Ruby on Rails you can add a require statement in your Gemfile. This includes an Ruby on Rails engine which allows you to require the JavaScript.
gem 'mountable_file_server', '~> 0.0.2', require: 'mountable_file_server/rails'
//= require mountable_file_server
Once installed it is time to run the server so file uploads can be made. The server is called MountableFileServer::Endpoint
and is a minimal Sinatra application which can be run as usual. You can also mount an endpoint inside your existing application. With Ruby on Rails this would look like this.
Rails.application.routes.draw do
mount MountableFileServer::Endpoint, at: MountableFileServer.configuration.mounted_at
end
As seen in the previous section there is a global configuration at MountableFileServer.configuration
available.
This is an instance of the MountableFileServer::Configration
class. The global configuration is a default argument for all classes that require access to the configuration. In situations where you have multiple endpoints with different settings you can pass in you own configuration objects instead.
The global configuration can be configured through a block.
MountableFileServer.configure do |configuration|
configuration.mounted_at = '/uploads'
configuration.stored_at = '/path/to/desired/uploads/directory'
end
In order to build correct URLs for public files the mounted_at
attribute should be set to the path where the endpoint is mounted.
The stored_at
attribute tells the MountableFileServer where to store uploaded files. It will automatically create this and all needed subdirectories. This directory should not be within the root of your webserver. Otherwise private files are accessible to the internet.
The idea is to upload a file with AJAX rather than sending it with the usual form submit. After the upload, an unique identifier (uid
), is added to the form as a hidden element. Instead of dealing with a file upload your application only has to store the uid
, a simple string value.
Using the MountableFileServer::Adapter
your application can work with the uploaded file, the only thing needed is the uid
.
On the JavaScript side you use uploadFiles(element, files)
to upload files to the endpoint. The element
argument is a DOM element that needs two data attributes. data-endpoint
specifies where the AJAX request should be sent to, this probably matches the mounted_at
configuration option. data-type
tells the endpoint if the uploaded files should be treated as public or private files. Possible values are public
or private
.
The files
argument is an array of File
objects or an FileList
object. These objects are for example returned when a user selects files using an input
element.
Following events will be dispatched on the element that was passed to uploadFiles
. When you are listening to one of these events you can access the described attributes on the event.detail
object.
upload:start
is dispatched when the upload starts.
It has the attributes uploadId
and file
. The uploadId
is local and can be used to identify events in a scenario where multiple files are uploaded. The file
attribute is the original File
object and useful for showing a preview or other information about the file.
upload:progress
is continuously dispatched while the upload is happening.
It has the attributes uploadId
and progress
. The progress
attribute is the original ProgressEvent object of the AJAX request.
upload:success
is dispatched when the upload succeeded.
It has the attributes uploadId
, uid
and wasLastUpload
. The uid
attribute is the unique identifier generated by the MountableFileServer. You will want to add it to your form and store it along your other data. The wasLastUpload
attribute indicates if this was the last upload in progress.
The MountableFileServer::Adapter
class allows you to interact with uploaded files. It takes a MountableFileServer::Configuration
instance as argument and uses MountableFileServer.configuration
by default.
MountableFileServer::Adapter#store_temporary(input, type, extension)
Stores the input as file in the temporary storage and returns the uid
of the file. input
can be a path to a file or an IO object. type
can be public
or private
and the extension
argument specifies the extension the file should have.
MountableFileServer::Adapter#store_permanent(input, type, extension)
Stores the input as file in the permanent storage and returns the uid
of the file. input
can be a path to a file or an IO object. type
can be public
or private
and the extension
argument specifies the extension the file should have.
MountableFileServer::Adapter#move_to_permanent_storage(uid)
Moves a file from the temporary storage to the permanent one. This is mostly used in a scenario where users upload files. A file uploaded through the endpoint is initially only stored in the temporary storage. The application has to move it explicitly to the permanent storage. For example after all validations passed.
MountableFileServer::Adapter#remove_from_permanent_storage(uid)
Removes the file from the permanent storage.
MountableFileServer::Adapter#url_for(uid)
Returns the URL for an uploaded file. Only works for public files, if you pass the uid
of a private file an error will be raised.
MountableFileServer::Adapter#pathname_for(id)
Returns a Pathname object for the uploaded file. The pathname will always point to the file on disk independent from the files type or current storage location.
Run the migrations of the Ruby on Rails dummy application to make sure you can run the tests: cd test/rails-dummy && RAILS_ENV=test bundle exec rake db:migrate
.
Run tests with bundle exec rake test
.
- Increment
lib/mountable_image_server/version.rb
to your liking. - Make a Git commit.
- Run
bundle exec rake release
.