Rails 4: How to upload files with AJAX
Have a look into the remotipart
gem: https://github.com/JangoSteve/remotipart -- may get you all of the way there with very little work!
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.
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 endend
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"