我已经很久没有在web应用程序的前端使用Rails了,当然我想使用最新的版本,但它似乎发生了很大的变化,我不知道哪种方式是最适合Rails的。
我试过使用JQuery和FileUpload插件,但我们再也没有JQuery了,我的意思是,我试过添加它,但使用新的导入图很痛苦(我有问题,我知道如果我查了一些教程,我可以做(,但这似乎违背了JS在rails应用程序中的当前心态。
然后我去签出了新的Hotwire+Stimulus,但我现在甚至不知道从哪里开始,但从我看到的一点来看,我不知道是否能处理这种情况:我的S3 Bucket
已经有了一个presigned_url
,只是有一个带有f.file_field
的表单,我想通过POST
请求将该文件从客户端浏览器直接上传到S3
,这样用户就不会在等待上传完成时被阻止
如果我错了,请纠正我,但要触发JS函数,Rails现在的方法是将Stimulus
与HTML Data Attributes
一起使用,但我不确定我是否可以传递该数据属性中的文件。
查看其他教程,我开始认为最好的方法是使用turbo_stream_tag
来包装我的表单,然后当提交表单时,它将作为ajax请求,使用Net:HTTP
甚至s3
gem本身异步运行以进行后请求,我只是不确定我是否可以访问该文件。
有什么好心人可以澄清这一点吗?谢谢你的长帖,很抱歉。
如果你还没有看过,你可能会想看看这个:https://edgeguides.rubyonrails.org/active_storage_overview.html#example
这里是一个带有importmaps的默认Rails7设置的入门程序。主要取自上面的示例链接,并包含在刺激计划中。
# config/importmap.rb
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "@rails/activestorage", to: "https://ga.jspm.io/npm:@rails/activestorage@7.0.2/app/assets/javascripts/activestorage.esm.js"
<!-- inside your form -->
<div data-controller="upload">
<%= form.file_field :avatar, direct_upload: true,
data: { upload_target: "input",
action: "change->upload#uploadFile" } %>
<div data-upload-target="progress"></div>
</div>
一旦选择文件,就会开始上传。对于Turbo,它是非阻塞的。只要浏览器没有刷新,它就会上传。
// app/javascript/controllers/upload_controller.js
import { Controller } from "@hotwired/stimulus";
import { DirectUpload } from "@rails/activestorage";
export default class extends Controller {
static targets = ["input", "progress"];
uploadFile() {
Array.from(this.inputTarget.files).forEach((file) => {
const upload = new DirectUpload(
file,
this.inputTarget.dataset.directUploadUrl,
this // callback directUploadWillStoreFileWithXHR(request)
);
upload.create((error, blob) => {
if (error) {
console.log(error);
} else {
this.createHiddenBlobInput(blob);
// if you're not submitting the form after upload, you need to attach
// uploaded blob to some model here and skip hidden input.
}
});
});
}
// add blob id to be submitted with the form
createHiddenBlobInput(blob) {
const hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("value", blob.signed_id);
hiddenField.name = this.inputTarget.name;
this.element.appendChild(hiddenField);
}
directUploadWillStoreFileWithXHR(request) {
request.upload.addEventListener("progress", (event) => {
this.progressUpdate(event);
});
}
progressUpdate(event) {
const progress = (event.loaded / event.total) * 100;
this.progressTarget.innerHTML = progress;
// if you navigate away from the form, progress can still be displayed
// with something like this:
// document.querySelector("#global-progress").innerHTML = progress;
}
}
Sooooo,很抱歉刚才出现了延迟,这是实现主动存储直接上传的最简单、最前沿的方法,它非常简单,尽管文档并不清楚如何使用Stimulus实现它。因此,如果你遵循这些文档,并且你想让它与Stimulus一起工作,请确保用这一节覆盖javascript,首先,创建Stimulus控制器,为你想要的操作添加事件侦听器,在我的例子中,它将是onDrop
和onChange
事件,然后将其与我们将创建的自定义上传器类绑定。
// rails generate stimulus direct_uploads
// => creates a js file under app/javascript/controllers/direct_uploads_controller.js
import { Controller } from '@hotwired/stimulus';
// don't worry about this import you'll find the class right below
import Uploader from '../utils/direct_uploads';
// Connects to data-controller="direct-uploads"
export default class extends Controller {
connect() {
}
// drop->direct-uploads#handleInputChange
handleOnDrop(event) {
event.preventDefault();
const { files } = event.dataTransfer;
Array.from(files).forEach((file) => this.uploadFile(file));
}
// change->direct-uploads#handleInputChange
handleInputChange({ currentTarget }) {
const { files } = currentTarget;
Array.from(files).forEach((file) => this.uploadFile(file, currentTarget));
}
uploadFile(file, currentTarget) {
const upload = new Uploader(file, currentTarget);
upload.start();
}
}
// now let's create the uploader class.
// => app/javascript/utils/direct_uploads.js
import { DirectUpload } from '@rails/activestorage';
export default class Uploader {
constructor(file, currentTarget) {
const { dataset: { directUploadUrl } } = currentTarget;
this.directUpload = new DirectUpload(file, directUploadUrl, this);
this.targetForm = currentTarget.closest('form');
this.file = file;
this.currentTarget = currentTarget;
}
start() {
this.directUpload.create((error, blob) => {
if (error) {
// Handle the error
} else {
// Add an appropriately-named hidden input to the form
// with a value of blob.signed_id
}
});
}
directUploadWillStoreFileWithXHR(request) {
request.upload.addEventListener(
'progress',
(event) => this.directUploadDidProgress(event),
);
}
directUploadDidProgress(event) {
// Use event.loaded and event.total to update the progress bar
}
}
如果你知道更好的方法,请告诉我,不使用任何额外的库