Awesome
Shrine Tus Demo
This is a Ruby demo app for integrating the tus resumable upload protocol with Shrine. It uses Uppy powered by tus-js-client for resumable uploads to a tus-ruby-server, and attaches the uploaded file with the help of shrine-tus. The app runs on the Falcon web server, because tus-ruby-server benefits from Falcon's asynchronous streaming of requests and responses.
Setup
- Run
bundle install
- Run
bundle exec falcon serve
Guide
Configuring tus-ruby-server
First you'll need to add the necessary gems to your Gemfile:
# Gemfile
gem "shrine", "~> 2.0" # File attachments
gem "tus-server", "~> 2.0" # HTTP API for tus resumable upload protocol
gem "shrine-tus", "~> 1.2" # Glue between Shrine and tus-server
In this demo we're running the tus server alongside our main application. The
tus server is mounted on /files
, so this has to be the value of the
:endpoint
option for the tus client.
By default the tus server will save uploaded files to the data/
directory on
disk. However, you can still choose a different directory, or even a different
storage; see the documentation for more details.
Note that the storage that the tus server uses will only be temporary; once the
file is uploaded to tus-ruby-server
and assigned as record's attachment,
Shrine will take that file and re-upload it to the Shrine's permanent storage.
This separation allows you to easily clear unfinished or unattached
uploads
Configuring Shrine and shrine-tus
This demo uses the "Approach A" of hooking up shrine-tus
, which is to use
Shrine::Storage::Tus
(subclass of Shrine::Storage::Url
) for the temporary
Shrine storage. This approach is nice because it decouples your app from the
tus server implementation.
The Shrine storages can be configured in a way that allows you to use the tus server only for some attachments (normally large ones), while other attachments can still accept files in the standard way.
require "shrine"
require "shrine/storage/file_system"
require "shrine/storage/tus"
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"),
tus: Shrine::Storage::Tus.new,
}
Then you can add a TusUploader
abstract class which changes the default
temporary storage to Shrine::Storage::Tus
:
class TusUploader < Shrine
# use Shrine::Storage::Tus for temporary storage
storages[:cache] = storages[:tus]
end
Now every uploader that you want to accept uploaded files from tus server can
inherit from TusUploader
:
class VideoUploader < TusUploader
# ...
end
class Movie < Sequel::Model
include VideoUploader::Attachment.new(:video)
end
JavaScript
If you're familar with the flow for direct uploads in Shrine, integrating
tus-ruby-server
with Shrine is very similar; on the client side you use a
specialized library that talks the tus resumable upload protocol, and assign
the uploaded file data instead of the raw file. This is how it works on the
high level:
- tus client (
tus-js-client
) uploads a file to the tus server (tus-ruby-server
) - tus server returns the URL of the finished upload to the client
- client submits the tus URL and file metadata to your app
- uploaded file data is assigned as a Shrine attachment
When the client finishes uploading the file to the tus server, it should send the uploaded file information to the server in the Shrine uploaded file JSON format, so that it can be attached to the record.
{
"id": "http://localhost:9292/files/a0099970b2228b936e2fb413dffb402d",
"storage": "cache",
"metadata": {
"filename": "nature.jpg",
"size": 1342347,
"mime_type": "image/jpeg"
}
}
You can assign the uploaded file data to the hidden attachment field to be submitted with the form, or send it immediately to the app in an AJAX request. The only important thing is that the param name matches Shrine's attachment attribute name.
Attachment
Since the hash format above matches Shrine's uploaded file format, on the server side you can now assign this file data directly to the attachment attribute on the record.
file_data #=> "{\"id\":\"http://localhost:9292/files/68db42638388ae645ab747b36a837a79\",\"storage\":\"cache\",\"metadata\":{...}}"
Movie.create(video: file_data)
You can use the backgrounding Shrine plugin if you want the file to be re-uploaded from tus server to permanent storage in a background job. This is especially useful if you also want to do some file processing before uploading to permanent storage.
For various options regarding integrating Shrine and tus-ruby-server see the shrine-tus documentation.