Skip to content

Instantly share code, notes, and snippets.

@brenogazzola
Created February 6, 2023 20:30
Show Gist options
  • Save brenogazzola/8fd33498e28432d4c2efcb120269b02a to your computer and use it in GitHub Desktop.
Save brenogazzola/8fd33498e28432d4c2efcb120269b02a to your computer and use it in GitHub Desktop.
Manual Multiple File Direct Upload
import { DirectUpload } from '../rails/activestorage'
export default class {
constructor (input) {
this.input = input
this.submit = input.form.querySelector('.btn-submit') // The submit button. We show loading progress here
this.progress = this.submit.querySelector('.btn-submit-progress') // The progress bar inside the submit
this.loader = this.submit.querySelector('.btn-submit-loader') // The loader animation inside the submit
this.totalSize = 0
this.loaded = {}
}
upload () {
// For each file that needs to be uploaded, create a promise
const promises = Array.from(this.input.files).map(file => {
return new Promise((resolve, reject) => {
const url = this.input.dataset.directUploadUrl
this.submit.classList.add('btn-submit-uploading')
this.totalSize += file.size
this.directUpload(this.submit, this.input, file, url, resolve, reject, this)
}).catch(() => {
// Do nothing. Yes nothing, I just want Sentry to shut up.
})
})
// Wait until all promises are done before returning controle to the JS library
return Promise.allSettled(promises).catch(() => {
// Do nothing. Yes nothing, I just want Sentry to shut up.
})
}
// The code that actually handles the direct upload. Notice that each file is given a hidden input field with its signed_id
directUpload (submit, input, file, url, resolve, reject, caller) {
new DirectUpload(file, url, caller).create((error, blob) => {
if (error) {
submit.classList.remove('btn-submit-uploading')
reject(error)
} else {
const hiddenField = document.createElement('input')
hiddenField.setAttribute('type', 'hidden')
hiddenField.setAttribute('value', blob.signed_id)
hiddenField.name = input.name
input.form.appendChild(hiddenField)
resolve('Upload done')
}
})
}
// Callback that indicates that direct upload is beginning
directUploadWillStoreFileWithXHR (request) {
request.upload.addEventListener('progress',
event => this.directUploadDidProgress(event))
}
// Callback that direct upload is in progress
directUploadDidProgress (event) {
this.loaded[event.total] = event.loaded // No ids, so let's do something fugly and use the file size as the id
const totalLoaded = Object.values(this.loaded).reduce((sum, x) => sum + x)
this.progress.style.width = `${totalLoaded / this.totalSize * 100}%`
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment