Skip to main content
Ask A Question
Notifications
You’re not receiving notifications from this thread.
Subscribe

How do I integrate Uppy.io or Dropzone.js into Rails with ActiveStorage?

Gems / Libraries • Asked by Ivan Raszl

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!


This would be great. I'll try and do an episode on Uppy soon.



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.



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.


I also would like to +1 an episode on this


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.


That's awesome, Chris...can't wait to watch the episode!



Ask and you shall receive! Here's the Uppy episode: https://gorails.com/episodes/uppy-with-active-storage



Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 29,763+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.