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

Hello Monks, My cgi scripts are all on unix. I create dynamic data on the fly. I am dreaming of a button such that the person clicks on it, it would open a regular window saying where they want to save this dynamic data. How can I do this? If this is possible does that mean I have to create a temporary file on unix drive? Thanks for all your help

20050205 Edit by castaway: Changed title from 'Perl/CGI : Need to Save Files into Windows'

Replies are listed 'Best First'.
Re: Need to Save Files into Windows
by punch_card_don (Curate) on Feb 03, 2005 at 16:13 UTC
    Disposition headers are your friend.

    No need for a new button. The button that the user pressed to launch the dynamic data generation is all you need. Then, when you're ready to return the newly generated data, preceed it with a disposition header:

    print "Content-Disposition: attachment; filename=$filename\n\n";
    It will cause the user's browser to open a dialog asking them what to do with it - open it or save it. The user selecting "save" will open the file save dialog.

    There are companion headers that can make it even more slick, such as content-length so the download dialog can display progress.

    Forget that fear of gravity,
    Get a little savagery in your life.

      It will cause the user's browser to open a dialog asking them what to do with it - open it or save it.
      Certainly correct. But still it is up to the browser what to do with the file. It might decide to silently open the file and display in the browser window if a suitable plugin is installed. Especially IE is known for such a behaviour.

      holli, regexed monk
        Could you expand on this statement? Do you mean that despite the header the browser might not open the open/save dialog at all, or do you mean that despite the user selecting "open" in the open/save dialog the browser might ignore that and just open the file anyway?

        I have various download scripts that download Word, RTF, PDF, Excel, PowerPoint and CSV files using the disposition header to ~1,000 users with every brand and version of browser imaginable. In 3 years I've never received a complaint that the dialog didn't open or the file wouldn't save when instructed to, but if there's a particular configuration that's at risk, I'd love to read about it.

        Forget that fear of gravity,
        Get a little savagery in your life.

      SOOOOOOO AMAZING!!!! Thanks
Re: Need to Save Files into Windows
by FitTrend (Pilgrim) on Feb 03, 2005 at 16:39 UTC

    The method that I use is to create the file in a directory then use javascript to redirect on a form button click that attempts to download it. Once the user finishes the download and clicks another button, I clean up the file(s).

    Something to keep in mind. If multiple users are using this application, then there may be file overlap. You will need to make the filename related to the user either by a username (if via .htaccess) or remote ip address.

    Sorry for the pseudo code, but I don't know what other work you've done. I'm basing this of an apache 2.0 implementation. I've capitalized the pseudo code for you. I also assume that you are already printing content to a web page before and after this snippet. I am also assuming the file contents are created and pushed to an array called @fileContentForUser. Since you can use CGI.pm or other methods, you would need to update some code here to reflect that.

    if (DLBUTTON eq 'DOWNLOADFILE') { if (PEOPLE LOGIN USING .HTACCESS) { $fileName = "$ENV{'REMOTE_USER'}-output.txt"; } elsif (USING REMOTE ADDRESS) { $fileName = "$ENV{'REMOTE_ADDR'}-output.txt"; } open (CONTENT, ">/html/downloads/$fileName"); while (@fileContentForUser) { # PERFORM ANY FORMATTING OR OTHER PER LINE STUFF HERE print CONTENT "$_\n"; } print qq { <script language=javascript><!-- window.open('/downloads/$fileName','_blank','') //--></script> };

    The code for the button on the webpage would be:

    <input type=button value="DOWNLOAD" onclick="window.open('/cgi-bin/SCR +IPT_NAME?DLBUTTON=DOWNLOADFILE','','')"

    if you want to clean up these files that could contain different names, then I would use (not the most efficient, but it works):

    opendir (DIR, '/html/downloads/'); @files = grep /-output.txt/, readdir (DIR); closedir(DIR); foreach (@files) { unlink ("/html/downloads/$_"); }

    The other issue you may run into is if the user has a file associated in the browser. If they open text files in the browser instead of downloading them to the drive, the contents will simply open inside the browser. The best thing I find is to zip up or tar the file first. Of course this will depend on your end users.

    I hope this helps!

    Sincerely,
    Marc
    www.marcbilodeau.com