9

Ive got an anchor link

<a href="http://bucket_name.amazonaws.com/uploads/users/4/songs/7/test.mp3">Download</a> 

How do I make it so when a user clicks on it, it actually opens a popup asking the user to save the file instead of trying to play the file on the browser?

EDIT:

I was reading this article.

  def download
    data = open(Song.first.attachment)
    send_data data.read, :type => data.content_type, :x_sendfile=>true
  end

The article suggest using x_sendfile, since send_file takes up an http process with the potential risk of hanging the app until the download is completed.

Second, I am using send_data instead of send_file, which seems to work if the file is remote (i.e. hosted on Amazon S3). As suggested by this article.

The article, I mentioned was posted on 2009. Is x_sendfile=>true still necessary? Will it hang the app if it isn't included?

Should I really be using send_data or send_file?

Christian Fazzini
  • 19,613
  • 21
  • 110
  • 215
  • Nice solution, thanks. The only change I had to make was tu use data = open(Song.first.attachment_url), because otherwise I was getting an error: 'can't convert Paperclip::Attachment into String'. – santuxus Nov 22 '12 at 09:28

3 Answers3

8

You can manage your file downloading with separate controller, if you don't want to eal with HTTP server configurations.

So you can send_file with disposition option as attachment.

fl00r
  • 82,987
  • 33
  • 217
  • 237
  • Does this take up a signle http thread? i.e. Will the app hang until the file download is completed? – Christian Fazzini May 13 '11 at 12:08
  • 1
    I get: Cannot read file http://actual_bucket_name.s3.amazonaws.com/uploads/users/4/songs/7/test.mp3. My method looks like: def download send_file Song.first.attachment, :type=>"application/mp3", :x_sendfile=>true, :disposition => 'inline' end – Christian Fazzini May 13 '11 at 15:08
5

Depends on how you / where you serve the file itself. I do not have experience with ruby but if you can alter the headers(most platforms offer this option) of the http response you can force a download. This requires:

Content-Type: application/force-download

I guess it will use "Content-type: application/octet-stream" by default which will cause the browser to play it.

But this will only work if you have control over the server/location that holds the actual file since you need to change the response when the file is sent to the browser.

BossJeroen
  • 51
  • 1
2

Skip The Controller Action

You don't even need the download controller action, you can just generate a download-friendly link like so:

In your attachment.rb

def download_url
  S3 = AWS::S3.new.buckets[ 'bucket_name' ] # This can be done elsewhere as well,
                                            # e.g config/environments/development.rb

  url_options = { 
    expires_in:                   60.minutes, 
    use_ssl:                      true, 
    response_content_disposition: "attachment; filename=\"#{file_name}\""
  }

  S3.objects[ self.path ].url_for( :read, url_options ).to_s
end

In your views

<%= link_to 'Download Avicii by Avicii', attachment.download_url %>

If you still wanted to keep your download action for some reason then just use this:

In your attachments_controller.rb

def download
  redirect_to @attachment.download_url
end

Thanks to guilleva for his guidance.

Community
  • 1
  • 1
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
  • The 'model method' above doesn't work as-is. The `url_for` method in the aws-sdk is used on an S3Object whereas the `s3` variable declared above just fetches the bucket. You'll need to add `.objects[ 'key' ]` to the end of that line for it to work. – Dan Weaver Dec 05 '16 at 21:55
  • 1
    @DanWeaver You're absolutely right. I've updated it to reference the object instead of just the bucket now. Have a looksy. – Joshua Pinter Dec 06 '16 at 00:36
  • I get a dynamic-constant-assignment error when I try this. – ddonche Jul 11 '18 at 06:47
  • @ddonche Try moving `S3 = AWS::S3.new.buckets[ 'bucket_name' ]` to the top of your `attachment.rb` file so it's not redefined every time `#download` is called or using lowercase `s3`. – Joshua Pinter Jul 11 '18 at 14:41