How do I integrate Uppy.io or Dropzone.js into Rails with ActiveStorage?
Uppy and Dropzone and popular js libraries that provide rich user experience for file uploads. ActiveStorage supports the integration of such libraries, but I couldn't find an example online on how to do it in practice. There are no active gems to implement it easily either.
Can you share your experiences if you had success building such an integration?
It would be awesome if Chris could create a video on this subject. I think it would be a hit video!
Just wanted to +1 Ivan's request for a video on using Uppy with ActiveStorage. I've found a decent guide on how to implement it by circumventing ActiveStorage direct uploads altogether and another on how to do it with Shrine, but since I spent the time learning (with Chris' expert guidance) how to implement ActiveStorage's built-in direct uploads on various Rails forms, I'd kind of like to keep as much of the ActiveStorage process in place as possible rather than use the Form, XHR, or Amazon S3 Uppy modules to replicate this.
It seems like I'm not the only person out there that wants to use Uppy as a nice file selector interface but defer to ActiveStorage for the actual upload and form submissions.
https://github.com/transloadit/uppy/issues/703
Their solution makes sense, but I'm getting a bit hung up on how to iterate over the files in the Uppy state object and pass them along to my Rails form's documents[]
input.
Have any of you guys checked out this repo? https://github.com/Sology/uppy-activestorage-upload
I looked at it briefly but I need to play around with it to see if it would work with my use case. I have a form to create or update a task with some controller business to close out open documentation requests when a user submits, and I was having trouble getting the Uppy files[]
input data and task data to come over in the params. I'll probably need to mess around to with the Uppy Form
module in combination with this gem.
Is the best way to get Rails.app.routes.url_helpers.rails_direct_uploads_path
into the Uppy js to just include it as a data attribute on a form element? It's already on the ActiveStorage file input but that's currently being targetted and replaced by Uppy.
Just wanted to share my solution using the uppy-activestorage-upload
plugin in case it is useful to anyone else. You basically just need to pull out the signed IDs returned from S3 after Uppy uploads the documents and create hidden form fields to send the signed IDs to Rails. By doing this, your params will look exactly like they would if you used a regular multiselect file input and you won't have to change anything in your Rails controller. I'm pretty new to Javascript to please forgive code smells.
One problem I had with Uppy's webcam plugin is that it creates a Blob
data object rather than a File
data object in Uppy's files
array when the webcam image is added, and this blob doesn't have the name
attribute that uppy-activestorage-upload
depends on when handling the direct uploads to S3 to get the signed IDs. Fortunately, the below Github issue clued me into Uppy's onBeforeFileAdded
option which you can use to set the blob name when a file is added.
Fix issue with webcam uploads
import Uppy from '@uppy/core'
import Dashboard from '@uppy/dashboard'
import Webcam from '@uppy/webcam'
import ActiveStorageUpload from 'uppy-activestorage-upload'
import '@uppy/core/dist/style.css'
import '@uppy/dashboard/dist/style.css'
import '@uppy/webcam/dist/style.css'
document.addEventListener('turbolinks:load', function() {
const form = document.querySelector('#document_upload_form')
const file_input = document.querySelector('#documents')
const documentUploader = Uppy({
id: 'document',
// set file name for blob captured from webcam
onBeforeFileAdded: (file, files) => {
if(!file.data.name) {
file.data.name = file.name
}
return file
}
})
.use(ActiveStorageUpload, {
directUploadUrl: form.dataset.directUploadUrl
})
.use(Dashboard, {
target: '#document_upload_field',
replaceTargetContent: false,
width: 840,
height: 300,
inline: true,
proudlyDisplayPoweredByUppy: false,
hideUploadButton: false,
})
.use(Webcam, {
target: Dashboard,
countdown: false,
modes: ['picture'],
mirror: false,
facingMode: 'environment',
})
documentUploader.on('complete', result => {
// Add hidden form field with signed id for each successful direct upload
result.successful.forEach( upload => {
insertSignedIdHiddenField(form, file_input, upload.response.signed_id)
})
// disable original file input
file_input.disabled = true
// submit form
document.getElementById('document_upload_form').submit();
})
function insertSignedIdHiddenField(form, input, signed_id) {
const hiddenField = document.createElement("input")
hiddenField.setAttribute("type", "hidden")
hiddenField.setAttribute("value", signed_id)
hiddenField.name = input.name
form.appendChild(hiddenField)
}
}, false);
Thanks for the info on webcam uploads Max!
I also just finished recording a screencast on using Uppy with ActiveStorage, so that episode should go live in the next week or so.
Ask and you shall receive! Here's the Uppy episode: https://gorails.com/episodes/uppy-with-active-storage