#ruby-on-rails #dropzone.js #stimulusjs
Вопрос:
У меня есть приложение ruby on rails со стимулом.js и dropzone.js для загрузки вложения. Теперь существует ограничение на загрузку одного файла, но это позволяет загружать более одного файла и просто выводит на них сообщение об ошибке. Мне нужно, чтобы было невозможно загрузить более одного файла, и если после этого пользователь попытается загрузить другой, произойдет замена.
dropzone_controller.js
import Dropzone from "dropzone"; import { Controller } from "stimulus"; import { DirectUpload } from "@rails/activestorage"; import { getMetaValue, toArray, findElement, removeElement, insertAfter } from "helpers"; export default class extends Controller { static targets = ["input"]; connect() { this.dropZone = createDropZone(this); this.hideFileInput(); this.bindEvents(); Dropzone.autoDiscover = false; // necessary quirk for Dropzone error in console } // Private hideFileInput() { this.inputTarget.disabled = true; this.inputTarget.style.display = "none"; } bindEvents() { this.dropZone.on("addedfile", file =gt; { setTimeout(() =gt; { file.accepted amp;amp; createDirectUploadController(this, file).start(); }, 500); }); this.dropZone.on("removedfile", file =gt; { file.controller amp;amp; removeElement(file.controller.hiddenInput); }); this.dropZone.on("canceled", file =gt; { file.controller amp;amp; file.controller.xhr.abort(); }); } get headers() { return { "X-CSRF-Token": getMetaValue("csrf-token") }; } get url() { return this.inputTarget.getAttribute("data-direct-upload-url"); } get maxFiles() { return this.data.get("maxFiles") || 1; } get maxFileSize() { return this.data.get("maxFileSize") || 256; } get acceptedFiles() { return this.data.get("acceptedFiles"); } get addRemoveLinks() { return this.data.get("addRemoveLinks") || true; } } class DirectUploadController { constructor(source, file) { this.directUpload = createDirectUpload(file, source.url, this); this.source = source; this.file = file; } start() { this.file.controller = this; this.hiddenInput = this.createHiddenInput(); this.directUpload.create((error, attributes) =gt; { if (error) { removeElement(this.hiddenInput); this.emitDropzoneError(error); } else { this.hiddenInput.value = attributes.signed_id; this.emitDropzoneSuccess(); } }); } createHiddenInput() { const input = document.createElement("input"); input.type = "hidden"; input.name = this.source.inputTarget.name; insertAfter(input, this.source.inputTarget); return input; } directUploadWillStoreFileWithXHR(xhr) { this.bindProgressEvent(xhr); this.emitDropzoneUploading(); } bindProgressEvent(xhr) { this.xhr = xhr; this.xhr.upload.addEventListener("progress", event =gt; this.uploadRequestDidProgress(event) ); } uploadRequestDidProgress(event) { const element = this.source.element; const progress = (event.loaded / event.total) * 100; findElement( this.file.previewTemplate, ".dz-upload" ).style.width = `${progress}%`; } emitDropzoneUploading() { this.file.status = Dropzone.UPLOADING; this.source.dropZone.emit("processing", this.file); } emitDropzoneError(error) { this.file.status = Dropzone.ERROR; this.source.dropZone.emit("error", this.file, error); this.source.dropZone.emit("complete", this.file); } emitDropzoneSuccess() { this.file.status = Dropzone.SUCCESS; this.source.dropZone.emit("success", this.file); this.source.dropZone.emit("complete", this.file); } } function createDirectUploadController(source, file) { return new DirectUploadController(source, file); } function createDirectUpload(file, url, controller) { return new DirectUpload(file, url, controller); } function createDropZone(controller) { return new Dropzone(controller.element, { url: controller.url, headers: controller.headers, maxFiles: controller.maxFiles, maxFilesize: controller.maxFileSize, acceptedFiles: controller.acceptedFiles, addRemoveLinks: controller.addRemoveLinks, autoQueue: false }); }
_form.html.erb
lt;div data-lite-visibility-target="dynamic" class="space-y-8 lt;%= @automate_task_report.attachment.present? ? '' : "hidden" %gt;" gt; lt;div class="form-group inverted"gt; lt;%= form.label :attachment, "Upload test execution results", class: "form-label" %gt; lt;button type="button" class="dropzone dropzone-default dz-clickable form-control form-file form-file-btn" data-controller="dropzone" data-dropzone-max-file-size="10" data-dropzone-max-files="1" data-dropzone-accepted-files=".xml,.html,.jpg,.jpeg,.png,.gif"gt; lt;%= form.file_field :attachment, direct_upload: true, data: { target: 'dropzone.input' } %gt; lt;div class="dropzone-msg dz-message needsclick flex m-0"gt; lt;% if @automate_task_report.attachment.attached? %gt; lt;%= form.hidden_field :attachment, value: @automate_task_report.attachment.signed_id %gt; lt;div class="mx-5 attachment vertical"gt; lt;%= link_to @automate_task_report.attachment, target: "_blank", class:"attachment-thumb" do %gt; lt;%= image_tag(@automate_task_report.attachment) %gt; lt;% end %gt; lt;%= link_to @automate_task_report.attachment.filename.to_s, @automate_task_report.attachment, target: "_blank", class:"attachment-name" %gt; lt;%= link_to @automate_task_report.attachment, download: @automate_task_report.attachment, class:"btn btn-primary attachment-btn" do %gt; lt;span class="icon text-icon-default icon-download"gt;lt;/spangt; lt;% end %gt; lt;/divgt; lt;% end %gt; lt;span class="icon text-icon-lg icon-file-image-plus-lg mr-3"gt;lt;/spangt; lt;div class="text-left mt-0"gt; lt;pgt;Upload a file or drag and droplt;/pgt; lt;p class="text-xs"gt;XML, HTML, PNG, JPG, GIF up to 10MBlt;/pgt; lt;/divgt; lt;/divgt; lt;/buttongt; lt;/divgt; lt;/divgt; lt;/divgt; lt;/divgt;
Я уже пробовал такие вещи, как
= { maxFiles: 1 };
но у меня это не сработало. Как я могу достичь ожидаемого результата?
Ответ №1:
Используйте обратный вызов «принять», чтобы проверить максимальное количество файлов:
accept: function(file, done) { if (/* do a check here */) { done(error message); // File is cancel. file.status = Dropzone.CANCELED; } }
ключевым моментом здесь является изменение статуса файла на отменено, чтобы сообщить об этом dropzone