Rails ActiveStorage DirectUpload回调问题:自定义类后文件无法上传
解决ActiveStorage自定义DirectUpload子类后无法触发上传的问题
我帮你排查下问题所在,主要是几个小疏漏导致上传没触发,咱们一步步修正:
问题根源分析
- MyUploader构造函数参数未正确赋值:你在构造函数里直接把
this.file和this.url传给DirectUpload,但这两个属性根本没在构造函数里初始化,导致DirectUpload拿到的是undefined,自然无法工作。 - 实例化后未触发上传操作:在
direct_uploads.js里创建了MyUploader实例,但没有调用它的upload方法,相当于只创建了对象却没启动上传任务。 - 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
相关产品推荐
相关产品推荐

