Rails 4:如何使用 AJAX 上传文件

Posted

技术标签:

【中文标题】Rails 4:如何使用 AJAX 上传文件【英文标题】:Rails 4: How to upload files with AJAX 【发布时间】:2013-09-06 20:59:22 【问题描述】:

我想用 AJAX 上传文件。在过去,我通过使用神奇的jQuery form plugin 来实现这一点,并且效果很好。目前我正在构建一个 Rails 应用程序并尝试以“Rails 方式”做事,所以我正在使用 Form Helper 和回形针 gem 添加文件附件。

rails docs 警告表单助手不适用于 AJAX 文件上传:

不同于其他表单制作一个异步文件上传表单不像 就像为 form_for 提供 remote: true 一样简单。使用 Ajax 表单 序列化由在浏览器中运行的 javascript 完成,并且 因为 JavaScript 不能从你的硬盘读取文件 无法上传。最常见的解决方法是使用不可见的 用作表单提交目标的 iframe。

似乎没有现成的解决方案。所以我想知道最聪明的做法是什么。好像我有几个选择:

    使用表单助手和 iframe 技巧。 使用表单助手 + 加载 jQuery 表单插件来提交文件(不确定这是否会与 Rails 的真实性令牌等配合使用) 使用表单助手 + 回形针 + [其他一些 gem] 来扩展它的功能以允许提交 AJAX 表单。

这三个似乎都是可能的。我对#3知之甚少,特别是[其他一些宝石]部分。我发现了两个类似的问题(this 和this),其中提到了 Pic-Upload 的一个分支 Uploadify,但它们都有 2 年的历史并处理 Rails 2 和 3(并且 Uploadify 多年来没有更新)。所以考虑到发生了多少变化,我认为这确实是一个全新的问题:

在 Rails 4 中使用 AJAX 上传文件的最佳方式是什么?

【问题讨论】:

我认为这句话清楚地解释了为什么你总是“使用神奇的 jQuery 表单插件”;) 关于在 Rails 的表单助手旁边使用它的最流畅方式有什么建议吗?我在想我不应该使用 :remote=>true 而是让表单插件完成工作。 那个插件太棒了——刚刚试了一下,效果很好。感谢您提供信息丰富的问题​​:) 【参考方案1】:

看看remotipart gem:https://github.com/JangoSteve/remotipart -- 只需很少的工作就能让您一路走好!

【讨论】:

很多未解决的问题,我特别被那些关于 rails 4 兼容性的人吓坏了 我使用了带有 rails 4 的 remotipart,图像已上传,但响应 update.js.erb 没有被执行。 PLz帮助 仅供参考,这也适用于带有 Turbolinks 5 的 Rails 5。【参考方案2】:

使用@rails/ujs。

查看(.html.erb):

<%= file_field_tag :file,  id: "ajax_file_upload" %>

控制器(_controller.rb):

def update
  @record = YourModel.find(params[:id])

  respond_to do |format|
    if @record.update_attributes(params[:your_model])
      format.json  render json:  success: true  
    else
      error_messages = @record.errors.messages.values.flatten
      format.json  render json:  success: false, errors: error_messages  
    end
  end
end

javascript(.js)

const uploadFile = element => 
  const formData = new FormData();
  formData.append("your_model[attribute_name]", element.target.files[0]);

  Rails.ajax(
    url: "your_model/:id",
    type: "PUT",
    beforeSend(xhr, options) 
      options.data = formData;
      return true;
    ,
    success: response => 
      if (response.success) 
        alert("File uploaded successfully");
      
      else 
        alert(response.errors.join("<br>"));
      
    ,
    error: () => 
      alert("ajax send error");
    
  );
;

const documentOnReady = () => 
  const fileField = document.getElementById("ajax_file_upload");
  if (fileField) 
    fileField.addEventListener("change", uploadFile);
  


document.addEventListener("turbolinks:load", documentOnReady);

注意:使用FormData时不需要在ajax中设置RequestHeader。

FormData 使用与编码类型设置为“multipart/form-data”时表单相同的格式

【讨论】:

【参考方案3】:

恕我直言,在使用 AJAX 处理上传文件时,Rails 并不完美,尤其是在您需要进度条的情况下。我的建议是使用 Javascript 通过 AJAX 请求提交表单,就像您在 (2) 中建议的那样。如果你熟悉 Javascript,你不会有很多问题。

我最近通过使用这个非常简单的 JS 库 https://github.com/hayageek/jquery-upload-file 使用了相同的方法,我在这里写了更多详细信息 http://www.alfredo.motta.name/upload-video-files-with-rails-paperclip-and-jquery-upload-file/

对于使用表单上传带有标题和描述的电影的应用程序,JS 代码如下所示:

$(document).ready(function() 
  var uploadObj = $("#movie_video").uploadFile(
    url: "/movies",
    multiple: false,
    fileName: "movie[video]",
    autoSubmit: false,
    formData: 
      "movie[title]": $('#movie_title').text(),
      "movie[description]": $('#movie_description').text()
    ,
    onSuccess:function(files,data,xhr)
    
      window.location.href = data.to;
    
  );

  $("#fileUpload").click(function(e) 
    e.preventDefault();
    $.rails.disableFormElements($($.rails.formSubmitSelector));
    uploadObj.startUpload();
  );
);

远非完美,但为您的前端提供了灵活性。

【讨论】:

以上是关于Rails 4:如何使用 AJAX 上传文件的主要内容,如果未能解决你的问题,请参考以下文章

jQuery 表单插件 + Ajax 文件上传 + Rails

Rails 4使用carrierwave上传多个图像或文件

使用 Remotipart 的 Rails AJAX 上传表单

Rails Valums Ajax 上传

在rails中使用回形针进行ajax上传的简单方法?

如何在 laravel 4.2 中使用带有自定义预过滤器的 AJAX 上传 CSV 文件