views:

62

answers:

4

Hello,

In my web page (rendered by Rails), I'd like to let the user right-click on a photo to bring up the browser's Save As dialog, to let the user save the photo to their hard drive.

However, the photos on my server have unusual filenames (long hex names) with no file extension. The filename prompt in the Save As dialog has this ugly filename. If the user hits save, they'll end up with a poorly-named file, with no file extension.

The web page is aware of the photo's real file name (the name that came off the camera, for example). Is there a way for me to programmatically override the Save As dialog's filename prompt with a filename of my choosing?

I'm aware of the Content-Dispostion header, and that via this header a filename can be specified. However, I think that in order to be able to make use of this header, I need to load/render the entire file to the browser. If the asset to be made available for download is a movie, that loading of the file could timeout the browser...like, if it's a 100meg video.

Thoughts?

-A

A: 

You could do some smart server side url rewrite, like for example rewriting foo.mpeg to youveryuglyfilenamewithoutextension.

Johan
A: 

Set the Content-Disposition to "attachment; filename="...that's fine. "attachment" explicitly means it's not to be rendered in the browser, file renaming works nonetheless (or possibly particularly for that case).

Nicolas78
Hi. (Preface this by saying the file to download is on a server other than the Rails server.) In my Rails controller, I can set the Content-Disposition header, but then how do I specify in the controller the URL of the file to be downloaded. Currently, I'm using r = Net::HTTP.get_response()@content = r.bodyrender :layout => false...and in my view I render <%= @content => and I do get a save as prompt...but only after the file has fully loaded. For a video file, this takes too long. But maybe I don't need to actually load the file?Thoughts?
lagouyn
ok sorry that's a Rails-specific problem which I can't say so much about. I retagged your question accordingly to draw more Rails-experts. Normally, you only send the header and then you start sending the binary data for the file you want to transmit. No idea if what you describe causes this type of behaviour, doesn't seem so.
Nicolas78
A: 

Why has it this ugly file name in the first place? Maybe we can suggest a better workaround if we know that reason.

If that name can not be changed, then I suggest to do a rewrite on the fly before displaying the download prompt.

Ruby8848
A: 

Hi there,

I think I understand the problem here because I encountered (and resolved) at least part of it myself not too long ago.

I have some large mp3's and I link to them on my website

A few problems

  • I needed to set my content-disposition header to attachment in order to prevent files from automatically streaming whenever a user clicked the download button
  • my files are on a remote server
  • my files are large (100MB)
  • large files can tie up rails controllers if not handled properly

Now, Michael Koziarsky advises in this article that the best way to keep your rails processes free when serving large files, is to create a download action in your controller, and the do something like this (note the use of x_sendfile=>true):

  def download
    send_file '/path/to/podcast.mp3', :type => 'application/octet-stream', :disposition => 'attachment', :filename=>'something.mp3', :x_sendfile=>true
  end

:x_sendfile tells apache to let the file through without tying up a rails controller process. The rest of the code sets the filename and the content-disposition header.

Great, but I'm on heroku, like everyone else nowadays. So I can't use x_sendfile.

I found that I couldn't modify the nginx configuration file either as it's locked down by heroku so it was not possible to get x-accel-redirect (nginx equivalent of x-sendfile) working

So, I decided to add a perl script (see below) to the cgi-bin on our asset-host and this script sets the content-disposition to attachment and gives our file a name too.

Instead of doing a restful download like this:

link_to "download", download_podcast_path(@podcast.mp3)

we just link to the mp3 making sure that we go in through the cgi-bin so that the perl script gets called on every mp3 that leaves the server

# I'm using haml
%a{:href=>"http://afmpodcast.com/cgi-bin/download.cgi?ID=#{@podcast.mp3}"}
  download

The result is that my rails controller is no longer called into action when someone downloads a file

I found the perl script here and chopped it up a bit to work for me:

#!/usr/local/bin/perl -wT

use CGI ':standard';
use CGI::Carp qw(fatalsToBrowser);

my $files_location;
my $ID;
my @fileholder;

$files_location = "../";

$ID = param('ID');

open(DLFILE, "<$files_location/$ID") || Error('open', 'file');
@fileholder = <DLFILE>;
close (DLFILE) || Error ('close', 'file');

print "Content-Type:application/x-download\n";
print "Content-Disposition:attachment;filename=$ID\n\n";
print @fileholder

My code, is on github but you'll likely have all sorts of problems using it on your machine as i make heavy use of ENV variables that I store in bashrc and I have no documentation or tests ^hides^

stephenmurdoch