无模型关联时,如何用Rails ActiveStorage direct_upload替代手动上传?
可以用ActiveStorage Direct Upload替代手动上传
完全可以用ActiveStorage的direct_upload方案替代当前的手动上传方式,这种方式能将文件直接上传到存储服务(如S3、本地磁盘),无需经过应用服务器中转,既减轻服务器负载,又能提供更好的上传体验(比如原生进度反馈)。下面是具体的改造步骤:
1. 改造前端表单
调整表单中的文件上传字段,启用direct_upload功能,并确保ActiveStorage的JavaScript已加载:
<%= form_with( multipart: true, scope: :ctf, url: some_path, id: dom_id(Import.new, :form) ) do |form| %> <%= render Content::SectionComponent.new do |section| %> <span class="text-sm"><%= 'Upload XML' %></span> <%= form.label :cf_file, 'File' %> <!-- 添加direct_upload: true启用直传 --> <%= form.file_field :cf_file, accept: 'application/xml', required: true, direct_upload: true %> <% end %> <% end %>
注意:确保你的应用已加载ActiveStorage的JS——Rails默认在
application.js中包含require("@rails/activestorage")(Importmaps环境则是import "@rails/activestorage")。如果之前禁用了Turbo,也可以保留data: { turbo: false },不影响direct_upload功能。
2. 调整控制器逻辑
Direct Upload完成后,表单提交的不再是原始文件对象,而是ActiveStorage Blob的signed_id。你需要通过这个signed_id获取对应的Blob,再传给后台任务:
先修改参数白名单
private def cf_upload_params params.require(:ctf).permit(:cf_file) # 此时:cf_file的值是Blob的signed_id字符串 end
重构cf_upload方法
def cf_upload importer = Import.new(cf_upload_params) if importer.valid? # 通过signed_id查找对应的Blob file_storage = ActiveStorage::Blob.find_signed(cf_upload_params[:cf_file]) Importers::Cf::ImportJob.perform_later(file_storage) flash.now[:notice] = '文件上传成功,正在后台导入数据' else flash.now[:alert] = '文件验证失败,请检查后重试' end render :index end
3. 适配模型验证逻辑
如果你的Import模型有文件类型、大小等验证,需要调整验证逻辑——因为现在拿到的是signed_id,而非文件对象。示例如下:
class Import include ActiveModel::Model attr_accessor :cf_file validate :valid_xml_file private def valid_xml_file return if cf_file.blank? begin blob = ActiveStorage::Blob.find_signed(cf_file) # 验证文件类型 unless blob.content_type == 'application/xml' || blob.filename.extension.downcase == 'xml' errors.add(:cf_file, '必须是XML格式的文件') end # 验证文件大小(示例:不超过10MB) if blob.byte_size > 10.megabytes errors.add(:cf_file, '文件大小不能超过10MB') end rescue ActiveStorage::Blob::InvalidKeyError errors.add(:cf_file, '无效的文件上传,请重新选择文件') end end end
4. 可选:添加前端上传反馈
ActiveStorage内置了direct_upload的事件监听,你可以自定义上传进度、错误提示等交互:
// 监听直传初始化事件 document.addEventListener('direct-upload:initialize', (event) => { const { file } = event.detail // 这里可以创建进度条元素显示给用户 console.log(`开始上传文件: ${file.name}`) }) // 监听上传进度事件 document.addEventListener('direct-upload:progress', (event) => { const { loaded, total } = event.detail const progress = Math.round((loaded / total) * 100) // 更新进度条的百分比 console.log(`上传进度: ${progress}%`) }) // 监听上传错误事件 document.addEventListener('direct-upload:error', (event) => { alert('文件上传失败,请检查网络或文件后重试') })
内容的提问来源于stack exchange,提问作者Suganya Selvarajan
相关产品推荐
相关产品推荐

