How to trigger download with Rails send_data from AJAX post

17,410

Solution 1

create a function in controller

def ajax_download
  send_file "path_to_file/" + params[:file]
end

and then in controller action

respond_to do |format|
  @java_url = "/home/ajax_download?file=#{file_name}"
  format.js {render :partial => "downloadFile"}
end

and make a partial in view folder name with _downloadFile.js.erb and write this line

window.location.href = "<%=@java_url %>"

Solution 2

You can't download a file to disk from JS. It's a security concern. See the blog post below for a good workaround.

http://johnculviner.com/post/2012/03/22/Ajax-like-feature-rich-file-downloads-with-jQuery-File-Download.aspx

Solution 3

Do not just copy and paste the accepted answer. It is a massive security risk that cannot be understated. Although the technique is clever, delivering a file based on a parameter anybody can enter allows access to any file anybody can imagine is lying around.

Here's an example of a more secure way to use the same technique. It assumes there is a user logged in who has an API token, but you should be able to adapt it to your own scenario.

In the action:

current_user.pending_download = file_name
current_user.save!
respond_to do |format|
  @java_url = "/ajax_download?token=#{current_user.api_token}"
  format.js {render :partial => "downloadFile"}
end

Create a function in the controller

def ajax_download
  if params[:token] == current_user.api_token
    send_file "path_to_file/" + current_user.pending_download
    current_user.pending_download = ''
    current_user.save!
  else
    redirect_to root_path, notice: "Unauthorized"
  end
end

Make a partial in view folder name with _downloadFile.js.erb

window.location.href = "<%=@java_url %>"

And of course you will need a route that points to /ajax_download in routes.rb

get 'ajax_download', to: 'controller#ajax_download'
Share:
17,410
mayatron
Author by

mayatron

Updated on June 15, 2022

Comments

  • mayatron
    mayatron almost 2 years

    I'm trying to use send_data to return a PNG image as the response for a ajax post request. How do I get the browser to trigger a download on the success callback?

    Details

    I'm generating a large base64 image using canvas.toDataURL(), and then posting it to Rails (v3.2.6). Rails decodes it to a binary PNG, and sends the image back to the client.

    I've also tried send_file but it has the same issue.

    Other options

    1. Download image client side: We can't do this because (1) Safari crashes on large base64 URLs, and (2) Safari does not yet support the download attribute on anchor tags which I would need to specify the downloaded image filename.

    2. Use a $.get instead of $.post: We can't do this because we need to send our canvas.toDataURL() with the request to the server. GET requests URIs have size limitations.