add multiple file uploads

This commit is contained in:
Nathan 2026-02-02 11:16:50 -05:00
parent ef79dabe59
commit 54f8733c02
3 changed files with 43 additions and 32 deletions

View file

@ -37,7 +37,7 @@ class Components::Uploads::Index < Components::Base
label(for: "dropzone-file-input", class: "btn btn-primary", style: "cursor: pointer;") do
render Primer::Beta::Octicon.new(icon: :upload, mr: 1)
plain "Upload File"
plain "Upload Files"
end
end
end
@ -101,7 +101,7 @@ class Components::Uploads::Index < Components::Base
def dropzone_form
form_with url: uploads_path, method: :post, multipart: true, data: { dropzone_form: true } do
input(type: "file", name: "file", id: "dropzone-file-input", data: { dropzone_input: true }, style: "display: none;")
input(type: "file", name: "files[]", id: "dropzone-file-input", multiple: true, data: { dropzone_input: true }, style: "display: none;")
end
end
end

View file

@ -15,28 +15,38 @@ class UploadsController < ApplicationController
end
def create
uploaded_file = params[:file]
uploaded_files = Array(params[:files]).reject(&:blank?)
if uploaded_file.blank?
redirect_to uploads_path, alert: "Please select a file to upload."
if uploaded_files.empty?
redirect_to uploads_path, alert: "Please select at least one file to upload."
return
end
success_count = 0
errors = []
uploaded_files.each do |uploaded_file|
blob = ActiveStorage::Blob.create_and_upload!(
io: uploaded_file.tempfile,
filename: uploaded_file.original_filename,
content_type: uploaded_file.content_type
)
@upload = current_user.uploads.create!(
current_user.uploads.create!(
blob: blob,
provenance: :web
)
redirect_to uploads_path, notice: "File uploaded successfully!"
success_count += 1
rescue StandardError => e
event = Sentry.capture_exception(e)
redirect_to uploads_path, alert: "Upload failed: #{e.message} (Error ID: #{event&.event_id})"
Sentry.capture_exception(e)
errors << "#{uploaded_file.original_filename}: #{e.message}"
end
if errors.any?
redirect_to uploads_path, alert: "#{success_count} file(s) uploaded. Errors: #{errors.join(', ')}"
else
redirect_to uploads_path, notice: "#{success_count} #{'file'.pluralize(success_count)} uploaded successfully!"
end
end
def destroy
@ -51,23 +61,25 @@ class UploadsController < ApplicationController
private
def check_quota
uploaded_file = params[:file]
return if uploaded_file.blank? # Let create action handle missing file
uploaded_files = Array(params[:files]).reject(&:blank?)
return if uploaded_files.empty? # Let create action handle missing files
quota_service = QuotaService.new(current_user)
file_size = uploaded_file.size
policy = quota_service.current_policy
total_size = uploaded_files.sum(&:size)
# Check per-file size limit
if file_size > policy.max_file_size
redirect_to uploads_path, alert: "File size (#{ActiveSupport::NumberHelper.number_to_human_size(file_size)}) exceeds your limit of #{ActiveSupport::NumberHelper.number_to_human_size(policy.max_file_size)} per file."
# Check per-file size limit for each file
uploaded_files.each do |uploaded_file|
if uploaded_file.size > policy.max_file_size
redirect_to uploads_path, alert: "File '#{uploaded_file.original_filename}' (#{ActiveSupport::NumberHelper.number_to_human_size(uploaded_file.size)}) exceeds your limit of #{ActiveSupport::NumberHelper.number_to_human_size(policy.max_file_size)} per file."
return
end
end
# Check if upload would exceed total storage quota
unless quota_service.can_upload?(file_size)
# Check if uploads would exceed total storage quota
unless quota_service.can_upload?(total_size)
usage = quota_service.current_usage
redirect_to uploads_path, alert: "Uploading this file would exceed your storage quota. You're using #{ActiveSupport::NumberHelper.number_to_human_size(usage[:storage_used])} of #{ActiveSupport::NumberHelper.number_to_human_size(usage[:storage_limit])}."
redirect_to uploads_path, alert: "Uploading these files would exceed your storage quota. You're using #{ActiveSupport::NumberHelper.number_to_human_size(usage[:storage_used])} of #{ActiveSupport::NumberHelper.number_to_human_size(usage[:storage_limit])}."
nil
end
end

View file

@ -22,10 +22,9 @@
initialized = true;
// Handle file input change
// Handle file input change (supports multiple files)
fileInput.addEventListener("change", (e) => {
const file = e.target.files[0];
if (file) {
if (e.target.files.length > 0) {
form.requestSubmit();
}
});
@ -77,7 +76,7 @@
dropzone.classList.add("file-dropzone");
const title = document.createElement("h1");
title.innerText = "Drop your file here";
title.innerText = "Drop your files here";
dropzone.appendChild(title);
document.body.appendChild(dropzone);