21

I want to upload files with AJAX. In the past I accomplished this by using the magical jQuery form plugin and it worked great. Currently I'm building a Rails app and trying to do things "The Rails Way" so I'm using the Form Helper and the paperclip gem to add file attachments.

The rails docs warn that the Form Helper does not work for AJAX file uploads:

Unlike other forms making an asynchronous file upload form is not as simple as providing form_for with remote: true. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.

It seems clear there's no off-the-shelf solution. So I'm wondering what's the smartest thing to do. Seems like I have several options:

  1. Use the form helper and the iframe trick.
  2. Use the form helper + load jQuery form plugin to submit the file (not sure if this will play nice with Rails's authenticity token, etc)
  3. Use the form helper + paperclip + [some other gem] to extend it's functionality to allow AJAX form submission.

All three seem possible. I know the least about #3, specifically the [some other gem] part. I found two similar questions (this and this) which mention a branch of Pic-Upload called Uploadify but those are both 2 years old and deal with Rails 2 and 3 (and Uploadify hasn't been updated in years). So given how much has changed, I think this is really a whole new question:

What's the best way to upload files with AJAX in Rails 4?

3
  • I think the quote clearly explains why you always did it "using the magical jQuery form plugin" ;) Commented Sep 3, 2013 at 16:37
  • Any advice on the smoothest way to use it along side Rails's form helper? I'm thinking I should NOT use the :remote=>true and just let the form plugin do the work. Commented Sep 3, 2013 at 16:46
  • That plugin is awesome-- just tried it out and working like a charm. Thanks for the informative question :) Commented Jul 14, 2014 at 6:28

3 Answers 3

8

Have a look into the remotipart gem: https://github.com/JangoSteve/remotipart -- may get you all of the way there with very little work!

Sign up to request clarification or add additional context in comments.

3 Comments

Lots of open issues, I was particularly scared off by those regarding rails 4 compatibility
I have used remotipart with rails 4, image got uploaded, but the response update.js.erb is not getting executed. PLz help
FYI this works in Rails 5 with Turbolinks 5 as well.
4

Using @rails/ujs.

view (.html.erb):

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

controller(_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);

Note: No need to setRequestHeader in ajax while using FormData.

FormData uses the same format a form would use if the encoding type were set to "multipart/form-data"

Comments

1

IMHO Rails is not perfect when dealing with upload files using AJAX, especially if you want a progress bar. My suggestion is to use Javascript for the form submission over an AJAX request like you suggested in (2). If you are comfortable with Javascript you will not have many problems.

I recently used the same approach by using this very simple JS library https://github.com/hayageek/jquery-upload-file and I wrote more details here http://www.alfredo.motta.name/upload-video-files-with-rails-paperclip-and-jquery-upload-file/

For an application with a form to upload a movie with title and description the JS code looks like follow:

$(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();
  });
});

Far from perfect, but gives you flexibility on your frontend.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.