Rails 7 从浏览器客户端查看并直接上传图片

Rails 7 view and direct upload image from browser client

我已经很久没有使用 Rails 作为 Web 应用程序的前端了,我当然想使用最新版本,但似乎已经发生了很多变化,我不知道不知道哪个是最 Rails 的方法了。

我试过使用 JQuery 和 FileUpload 插件,但我们没有 JQuery 了,我的意思是我试过添加它,但在屁股使用新的导入映射(我有问题,我知道如果我查找一些教程我可以做到),但这似乎与 rails 应用程序中 JS 当前的心态背道而驰。

然后我去检查新的 Hotwire + Stimulus,但我什至不知道从哪里开始,但从我看到的一点点不知道是否会处理这种情况:我的 S3 Bucket 已经有一个 presigned_url,并且只有一个带有 f.file_field 的表单,我想将此文件从客户端浏览器直接上传到 S3POST 请求,这样用户就不会在等待上传完成时被阻塞

如果我错了请纠正我但是要触发 JS 函数 Rails 现在的方法是使用 StimulusHTML Data Attributes 但我不确定我是否可以通过此数据属性中的文件。

查看其他教程,我开始认为最好的方法是使用 turbo_stream_tag 来包装我的表单,然后在提交表单时将点击此涡轮控制器,它将充当 ajax 请求,运行 使用 Net:HTTP 甚至 s3 gem 本身异步执行 post 请求,我只是不确定我是否会有权访问该文件。

有好心人来澄清一下吗?感谢和抱歉漫长的 post.

如果你还没有,你可能想看看这个:https://edgeguides.rubyonrails.org/active_storage_overview.html#example

这是一个带有默认 Rails 7 导入映射设置的启动器。主要取自上面的示例 link 并包含在 Stimulus 中。

# 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/javascripts/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 a 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;
  }
}