Activity
Uppy provides support for a number of different sources (webcam, Google Drive, Instagram, external links, etc), each with their own plugins. Some of the configurations are more complicated than others, but the webcam functionality is pretty straightforward
Import the JS and CSS for the webcam plugin to your Javascript file (the below is for webpacker)
import Webcam from '@uppy/webcam'
import '@uppy/webcam/dist/style.css'
Add the webcam plugin to your Uppy instance in your initializer:
yourUppyInstance.use(Webcam, {
target: Dashboard,
countdown: false,
modes: ['picture'],
mirror: false,
facingMode: 'environment',
})
I discovered a bit of a gotcha in that file data added to the Uppy dashboard via normal file picker interface and the webcam are slightly different. I had to do the following in my Uppy core initializer to add a file name to the blobs that are generated via the webcam. The blob data doesn't have a name attribute but it needs one.
let yourUppyInstance = 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
}
})
Thanks so much for this, Chris. It really helped me refactor my Javascript.
In case anyone else encounters this, I was experiencing some Turbolinks/Uppy madness and it seems to be related to not loading the pack tags for my Uppy-related webpacker pack in the head of the document (I was splitting it so that that the Uppy JS isn't loaded everywhere in the application). Someone in GoRails Slack mentioned using content_for :head
for the pack tags in the view and yield :head
in application.html.erb
, and that seems to have cleared things up. At least for now.
That's awesome, Chris...can't wait to watch the episode!
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);
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 +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.