Torgo has asked for the wisdom of the Perl Monks concerning the following question:

Here's something that has bothered me for a while.. When I want a CGI script to send the user a file it has generated, I have no problem getting it to send the file and make a browser try to save it to disk (giving it a header mimetype of "application/binary" usually does the trick) but the browser always wants to save the file as "whatever.cgi". Is there any way to specify a different default filename, or at least a different extension? I've looked through my docs but haven't found anything along these lines.

I write the code while the master is away!

Replies are listed 'Best First'.
(Guildenstern) Re: Getting CGI to name a file it returns
by Guildenstern (Deacon) on Jul 16, 2001 at 20:58 UTC
    Another way to do it is to use the Content-Disposition header.
    Set the mimetype to "application/octet-stream", then add a header like this: "Content-Disposition: inline;filename=$filename\n".
    Be sure to set the "Content-Length" header as well.

    Negaterd character class uber alles!
      Aha. I was sure there was a way to do it without faking out stupid browsers... there'd be hell to pay if ever anyone actually made a good, smart browser!

      Now that I know what to look for, I was searching through, I found some code dealing with Content-Disposition, and looks like they've got it covered through the Attachment parameter:
      print $query->header(-type=>'application/octet-stream', -attachment=>'foo.doc');

      I write the code while the master is away!
      inline;filename=$filename is exactly how I did things in this thread. It works great. When the user is first prompted to save the file, it says whatever.cgi, but when they click save (insted of "open from..") it has the correct file name ($filename).

      Just my $0.02

      _14k4 - (
Re: Getting CGI to name a file it returns
by Masem (Monsignor) on Jul 16, 2001 at 20:53 UTC
    I believe you need to somehow present the URL to the browser of the generated page as:
    Most servers (read: apache) don't try for the full path but instead descend level by level down the URL until they find something meanful (and grabbing .htaccess files along the way). When it reaches the level, it passes the remaining URL ("/myfile.txt?file=myfile.txt") info into a variable (I believe in $QUERY_STRING, but I can't verify this at the moment); will typically ignore the stuff until the '?', at which point it does it's own parsing. I'm about 99% sure meryln has a column on how to use virtual file structure information in a CGI statement.

    On the browser side, most browsers look at the last 'filename' before the '?' as what the page should be saved as, so in the URL above, the browser would try to save this as myfile.txt. Note, however, this is a browser-dependant feature, though the 3 major ones (IE, NS, Opera) all do it this way.

    So all you need to do is to make sure that the form that is used to generate this page has the appropriate extra file information in the FORM ACTION field.

    Dr. Michael K. Neylon - || "You've left the lens cap of your mind on again, Pinky" - The Brain
saving CGI output to a different name (boo)
by boo_radley (Parson) on Jul 16, 2001 at 20:48 UTC
    usually putting (uh... memory slipping...) false file information after the actual path will do the trick. so rather than have and most browsers will save the script's output as baz.doc. This trick should not affect the execution of your script under Apache or IIS.
Re: Getting CGI to name a file it returns
by merlyn (Sage) on Jul 16, 2001 at 23:34 UTC
Re: Getting CGI to name a file it returns
by bikeNomad (Priest) on Jul 16, 2001 at 20:53 UTC
    what the heck is application/binary ? It's probably better to use application/octet-stream, which is an accepted MIME type (though it doesn't mean much).

    And it's more likely that someone will have defined a handler for an accepted MIME type. For instance, I have one handler in my browser for application/octet-stream, and another handler for unknown MIME types. And they're different.

      One thing I've noticed with Content-type: application/octet-stream is that certain browsers will still try and load the file in the browser instead of thowing up a save as dialog. (Certain versions of IE will do this.) I usually use application/unknown, although I'm not sure that it's 100% successful.
      Sure octet-stream means something. It means that the output should be read as a stream of octets rather than a stream of binary and that you should be able to rely on recieving an even set of octets rather than an number of bits that doesn't resolve to an even octet. Of course, this is imposed by the IP protocol, so anything that comes down the pipe is technically an "octet-stream." It could be an indication that the content is not to be interpretted with split bytes (which IS done elsewhere) and that it is probably not plaintext.

      Just Another Perl Backpacker
        I don't think there is a way to send partial octets in HTTP.

        update: re-phrased to not sound so sarcastic.

Re: Getting CGI to name a file it returns
by lindex (Friar) on Jul 16, 2001 at 22:42 UTC
    Another trick that sometimes works but one that I would not suggest is to play with the filename of your script,and in like say in .htaccess (with apache) set your handler for .doc as a cgi-script etc etc ...
    Brought to you by that crazy but lovable guy... lindex
Re: Getting CGI to name a file it returns
by cLive ;-) (Prior) on Jul 17, 2001 at 11:59 UTC
    This may be too obvious, but why not output/copy the file to a randomly created temp dir in your web site and then print a Location header that points to it? Using CGI:
    print CGI::redirect('http;//my.url.of.temp.file');
    If MIME type is unknown, browser will then ask you to save, using same filename.

    Or am I missing something?

    cLive ;-)

      First you have to actually create a file under your server's DocumentRoot, which could not be as obvisous as it seems if you don't have the permissions.

      Second, the file is potentially visible by other users too, and you must remember to erase it. Otherwise the filesystem will fill up and the file would be at other client's perusal.

      Third, you must carefully choose how to create the temp dir, or the same problems/race conditions associate to temp files would potentially show up.

      Fourth, MIME headers are so cool in this context :-)

      Nonetheless your solution is probably the quickest, but a security-proof implementation can be more painful than it seems. <SIGNATURE>-- TIMTOWTDI</SIGNATURE>

        Apologies, I left out full details, but assumed from mentioning 'temp dir' that that was implied.

        1) if you chmod the temp dir 777, that's not a major issue(if you have no control over server) - security, see below

        2) that was why I mentioned temp - sorry, implied, but not stated

        3) just run a clean up on if every now and then to remove files older than x minutes... (also removes any files that can be theoretically placed there by other users)

        Funnily enough, I have to do this myself now, and I'm using a session cookie and piping the file from above the web root. But that seemed quickest at the time. So I'll be trying the QS version out...

        TIMTODI ;)

        cLive ;-)