You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails7中如何用Turbo Stream实现无自定义JS的文件上传进度显示?

问题

我有一个Rails 7的表单:

<%= form_with(model: doc, url: my_docs_path(@application, doc), id: "#{dom_id(doc)}_form", html: {multipart: true}) do |form| %>
<%= form.file_field :doc %>
<%= form.submit 'Upload Doc' %>
<% end %>

该表单可正常提交至DocsController#create,控制器中配置了format.turbo_stream,create.turbo_stream.erb运行良好,上传后无需自定义JS和整页刷新即可显示新文档记录。但大文件上传时用户会觉得页面“卡住”,想要实现类似以下代码的上传进度逻辑,但不想使用这段手动编写XHR的JS:

<form autocomplete="off" enctype="multipart/form-data">
<br/>
<input type="file" name="file" id="f1" accept="video/*" onchange="uploadFile('/file/{{.name}}')"/>
<span id="p">---</span>
<br/>
</form>

<script type="text/javascript">
window.uploadFile = function(url){
  var formData = new FormData();

  var fileInputElement = document.getElementById("f1");
  formData.append("file", fileInputElement.files[0]);

  var xhr = new XMLHttpRequest();
  xhr.open("POST", url);
  xhr.upload.onprogress = function (e) {
    if (e.lengthComputable) {
      document.getElementById("p").innerHTML = ""+((e.loaded/e.total)*100);
    }
  }
  xhr.upload.onloadstart = function (e) {
    document.getElementById("p").innerHTML = "0";
  }
  xhr.upload.onloadend = function (e) {
    document.getElementById("p").innerHTML = ""+e.loaded;
    document.location.href = '/';
  }
  xhr.send(formData);
}
</script>

请问:在Rails7中,不使用上述手动XHR的JS代码,能否通过xhr.upload.onprogress事件向用户显示上传进度百分比?


解答

可以实现,无需完全重写XHR上传逻辑,只需借助Turbo的事件钩子监听内置XHR的上传进度,步骤如下:

  1. 添加进度显示元素
    在表单内新增用于展示进度的元素,比如百分比文本和进度条:
<%= form_with(model: doc, url: my_docs_path(@application, doc), id: "#{dom_id(doc)}_form", html: {multipart: true}) do |form| %>
  <%= form.file_field :doc %>
  <div id="upload-progress" style="display: none; margin-top: 8px;">
    <span id="progress-percentage">0%</span>
    <div style="width: 0%; height: 4px; background: #007bff; transition: width 0.2s ease; margin-top: 4px;"></div>
  </div>
  <%= form.submit 'Upload Doc' %>
<% end %>
  1. 绑定Turbo的XHR事件监听
    Turbo处理表单提交时会自动创建XHR对象,我们可以通过turbo:before-fetch-request事件获取该对象,然后绑定upload.onprogress相关回调:
document.addEventListener('turbo:before-fetch-request', (event) => {
  const { xhr } = event.detail;
  const form = event.target;
  
  // 仅针对目标文件上传表单生效
  if (form.id === "<%= dom_id(doc) %>_form" && form.enctype === 'multipart/form-data') {
    const progressContainer = form.querySelector('#upload-progress');
    const progressText = form.querySelector('#progress-percentage');
    const progressBar = progressContainer.querySelector('div');

    xhr.upload.onloadstart = () => {
      progressContainer.style.display = 'block';
      progressText.textContent = '0%';
      progressBar.style.width = '0%';
    };

    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        progressText.textContent = `${percent}%`;
        progressBar.style.width = `${percent}%`;
      }
    };

    xhr.upload.onloadend = () => {
      // 可选:上传完成后延迟隐藏进度条,给用户反馈
      setTimeout(() => {
        progressContainer.style.display = 'none';
      }, 1000);
    };
  }
});
  1. 关键注意事项
  • 这段JS可放在表单所在页面的<script>标签中,或打包到应用全局JS文件里
  • 确保Rails 7默认启用的Turbo已正常加载
  • 若需适配多个表单,可将ID判断改为类名判断,提升通用性

这种方式既保留了Turbo原本的turbo_stream响应优势,又无需手动编写完整XHR逻辑,即可实现上传进度的实时展示。


内容的提问来源于stack exchange,提问作者Andrew Arrow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 15:50:27