Skip to content

Instantly share code, notes, and snippets.

@thedanbob
Last active November 24, 2021 15:57
Show Gist options
  • Save thedanbob/38454f9d5d1a413b0c8bb4fc3dcc077f to your computer and use it in GitHub Desktop.
Save thedanbob/38454f9d5d1a413b0c8bb4fc3dcc077f to your computer and use it in GitHub Desktop.
Integrating Dropzone with ActiveStorage direct uploads
<%= form_for @album, multipart: true do |f| %>
<div id="dz-container" class="dropzone">
<% @album.photos.includes(:blob).each do |photo| %>
<%= render 'preview', blob: photo, field: 'album[photos]' %>
<% end %>
<div class="fallback">
<%= f.file_field :photos, multiple: true, direct_upload: true %>
</div>
</div>
<%= f.submit 'Save' %>
<% end %>
<div class="dz-preview dz-image-preview">
<div class="dz-image"><%= image_tag(blob.representation(resize_to_fill: [120, 120])) %></div>
<div class="dz-details">
<div class="dz-size"><span><%= number_to_human_size(blob.byte_size) %></span></div>
<div class="dz-filename"><span><%= blob.filename %></span></div>
</div>
<a class="dz-remove remove-existing" href="#">Remove file</a>
<%= hidden_field_tag "#{field}[]", blob.signed_id %>
</div>
import Dropzone from 'dropzone' // dropzone@5.9.3
import { BlobRecord } from '@rails/activestorage/src/blob_record' // @rails/activestorage@6.1.4
import { FileChecksum } from '@rails/activestorage/src/file_checksum'
Dropzone.autoDiscover = false
document.addEventListener('DOMContentLoaded', function() {
let dz = document.querySelector('#dz-container')
if (dz) {
const fileInput = dz.querySelector('input[type="file"]')
let dropzone = new Dropzone(dz, {
url: (files) => files[0].blobRecord.directUploadData.url, // Unique URL to upload file, returned in step 1
method: 'put',
uploadMultiple: false,
chunking: false,
addRemoveLinks: true,
// Step 1: request an empty blob from the server
accept: (file, done) => {
if (file.size == 0) {
return done('File is empty (0 B).')
}
FileChecksum.create(file, (error, checksum) => {
if (error) {
return done(error)
}
const blobRecord = new BlobRecord(file, checksum, fileInput.dataset.directUploadUrl)
blobRecord.create(error => {
if (error) {
return done(error)
} else {
file.blobRecord = blobRecord // Store blob info in file object
return done()
}
})
})
}
})
// Step 2: add extra headers that ActiveStorage expects
dropzone.on('sending', (file, xhr) => {
xhr.responseType = 'text'
const { headers } = file.blobRecord.directUploadData
for (const key in headers) {
xhr.setRequestHeader(key, headers[key])
}
})
// Step 3: override submit method to send just file, not FormData
dropzone.origSubmitRequest = dropzone.submitRequest
dropzone.submitRequest = function(xhr, formData, files) {
dropzone.origSubmitRequest(xhr, files[0].slice())
}
// Step 4: add blob's signed_id to form
dropzone.on('success', (file) => {
const hiddenField = document.createElement('input')
hiddenField.type = 'hidden'
hiddenField.name = fileInput.name
hiddenField.value = file.blobRecord.attributes.signed_id
file.previewElement.appendChild(hiddenField) // gets removed if preview element is removed
})
// Remove existing files
dz.querySelectorAll('.remove-existing').forEach(el => {
el.addEventListener('click', e => {
e.preventDefault()
el.parentNode.remove()
}
})
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment