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

Rails ActiveStorage DirectUpload回调问题:自定义类后文件无法上传

解决ActiveStorage自定义DirectUpload子类后无法触发上传的问题

我帮你排查下问题所在,主要是几个小疏漏导致上传没触发,咱们一步步修正:

问题根源分析

  1. MyUploader构造函数参数未正确赋值:你在构造函数里直接把this.file和this.url传给DirectUpload,但这两个属性根本没在构造函数里初始化,导致DirectUpload拿到的是undefined,自然无法工作。
  2. 实例化后未触发上传操作:在direct_uploads.js里创建了MyUploader实例,但没有调用它的upload方法,相当于只创建了对象却没启动上传任务。
  3. upload方法参数冗余且逻辑错误:你的upload方法接收了file参数,但实际应该使用实例自身存储的file属性,参数传递反而导致逻辑混淆。

修正后的代码

1. 修复MyUploader.js

import { DirectUpload } from "activestorage"

class MyUploader {
  // 构造函数正确初始化file、url和input属性
  constructor(file, url, input) {
    this.file = file;
    this.url = url;
    this.input = input; // 传入input,方便后续设置hidden字段的name
    this.upload = new DirectUpload(this.file, this.url, this); // 用实例属性初始化DirectUpload
  }

  // 去掉冗余的file参数,直接用实例的this.file
  upload() {
    this.upload.create((error, blob) => {
      if (error) {
        // 这里可以添加错误提示逻辑,比如在页面显示错误信息
        console.error("上传失败:", error);
      } else {
        const hiddenField = document.createElement('input');
        hiddenField.setAttribute("type", "hidden");
        hiddenField.setAttribute("value", blob.signed_id);
        hiddenField.name = this.input.name; // 使用传入的input的name属性
        document.querySelector('#upload-elements').appendChild(hiddenField);
      }
    });
  }

  directUploadWillStoreFileWithXHR(request) {
    request.upload.addEventListener("progress", event => this.directUploadDidProgress(event));
  }

  directUploadDidProgress(event) {
    const progress = Math.round((event.loaded / event.total) * 100);
    console.log(`上传进度: ${progress}%`);
    // 这里可以添加进度条更新逻辑,比如修改DOM元素显示实时进度
  }
}

export { MyUploader };

2. 修复direct_uploads.js

import { MyUploader } from "./my_uploader"; // 注意路径要和你的文件结构匹配

const input = document.querySelector('input[type=file]');

// 如果你需要拖拽上传功能就保留这段,不需要可以删除
const onDrop = (event) => {
  event.preventDefault();
  const files = event.dataTransfer.files;
  Array.from(files).forEach(file => uploadFile(file));
}

input.addEventListener('change', (event) => {
  Array.from(input.files).forEach(file => uploadFile(file));
  input.value = null; // 清空input,允许重复选择相同文件
});

const uploadFile = (file) => {
  const url = input.dataset.directUploadUrl;
  // 创建MyUploader实例时传入input,方便后续设置hidden字段的name
  const uploader = new MyUploader(file, url, input);
  uploader.upload(); // 关键:调用upload方法触发上传!
}

3. 确认ERB表单的正确性

你的表单代码本身没问题,只需确保direct_upload: true已经正确生成了data-direct-upload-url属性:

<%= form_with url: elements_upload_path, local: true, id: "upload-elements" do |form| %>
  <span class="btn btn-primary btn-file">
    <%= form.file_field :images, multiple: true, direct_upload: true %>
    Select File(s)
  </span>
<% end %>

关键修正点总结

  • 构造函数必须把传入的file和url赋值给实例属性,这样DirectUpload才能拿到正确的参数
  • 实例化MyUploader后一定要调用upload()方法,这是触发上传的核心步骤
  • 把input传入MyUploader,避免硬编码表单字段名,让代码更健壮
  • 修正后direct-upload:progress相关的事件会正常触发,你可以在directUploadDidProgress里添加UI更新逻辑

内容的提问来源于stack exchange,提问作者Matt D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:06:31