Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Web Initiated File Download

by THRAK (Monk)
on Sep 06, 2001 at 23:37 UTC ( [id://110714]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working on a project that creates delimited files from a DB that need to be sent to the user via the browser. Currently I am testing this with ASCII CSV data, but eventually it will be delimited Kanji character data. I have things working up to the point of creating the text file and initiating the download, but it simply returns the text to the browser. My approach to this is pretty much lifted from this node which seems to be what I want to do, but it's not working. There was a reply to that node which said to use "attachment" instead of "inline" for the Content-disposition but that doesn't seem to work either. Also, I am not calling any other HTTP headers before this point. I'm passing a fully pathed file handle to this sub:
sub download_file { my ($filename) = @_; my $filesize = -s $filename; # print full header print "Content-disposition: inline; filename=$filename\n"; print "Content-Length: $filesize\n"; print "Content-Type: application/octet-stream\n\n"; # open in binmode open(READ,$filename) || die; binmode READ; # stream it out binmode STDOUT; while (<READ>) { print $_; } close(READ); # should always return true return(1); }
Once I get this working, I would like to try to stream the data directly from the data array from which the CSV file is created. Assuming that I can get the download pop-up dialog to function, I would think I should be able to do that. Yes?

Thanks.
-THRAK
www.polarlava.com

Replies are listed 'Best First'.
(Ovid) Re: Web Initiated File Download
by Ovid (Cardinal) on Sep 07, 2001 at 00:23 UTC

    From this Microsoft article:

    When you serve a document from a Web server, you might want to immediately prompt the user to save the file directly to the user's disk, without opening it in the browser. However, for known MIME (Multipurpose Internet Mail Extensions) types such as Microsoft Word ("application/ms-word"), the default behavior is to open the document in Internet Explorer.

    You can use the content-disposition header to override this default behavior. Its format is:

    
    Content-disposition: attachment; filename=fname.ext 

    You can also read RFC 1806 which may give you more information. According to the RFP, you don't specify the filename with an "inline" content-disposition. Further, "inline" is designed to render the data directly rather than prompt for a dialog box, which is what your issue appears to be. I'd try the following untested code (note that I am passing the MIME type rather than hard-coding it):

    sub download_file { my ( $filename, $mime ) = @_; if ( ! -e $filename ) { croak "$filename does not exist": } my $filesize = -s $filename; # print full header print "Content-disposition: attachment; filename=$filename\n"; print "Content-Length: $filesize\n"; print "Content-Type: $mime\n\n"; # open in binmode open READ, "< $filename" or croak "Cannot open $filename for r +eading: $!"; binmode READ; # stream it out binmode STDOUT; { local $/; print <READ>; } close(READ); # should always return true return(1); }

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Ovid, thanks. Working from your code snippet I rolled it into the code below. Changed it to use CGI header generation and also passing a default file name. Probably not good for everyone, but works well for what I'm doing. This worked in both IE5.5 & NS4.7x when I pass it a MIME of $mime_type = "text/tab-separated-values\; charset=us-ascii";
      One question, what does the localization of $/ do? I understand what they do independently, but why is this necessary?
      sub download_file { # REF: http://www.perlmonks.org/index.pl?node_id=110714&lastnode_id +=3628 my ( $send_file, $send_name, $mime ) = @_; die "$send_file does not exist." unless ( -e $send_file ); my $filesize = -s $send_file; print $query->header(-type=>$mime, -Content_disposition=>"attachment; fil +ename=$send_name", -Content_Length=>"$filesize" ); # open in binmode open READ, "< $send_file" or die "Cannot open $send_file for readi +ng: $!"; binmode READ; binmode STDOUT; # stream it out { local $/; print <READ>; } close(READ); return(1); # should always return true }


      -THRAK
      www.polarlava.com

        THRAK asked:

        What does the localization of $/ do?

        As you probably know, $/ is the input record separator. Typically, this is set to the newline on the system the program runs on. This causes records to be read in "one line" at a time. However, since you intend to send all of this data straight to the browser, I thought it would be more efficient to use "slurp mode". By using local $/, the input record separator is undef and this causes the print to send the entire file without needing a while loop. However, I put $/ in its own scope to ensure that it would not clobber whatever value it had originally been set to, since this is a global variable.

        Cheers,
        Ovid

        Vote for paco!

        Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Web Initiated File Download
by $code or die (Deacon) on Sep 07, 2001 at 07:06 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://110714]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (5)
As of 2024-04-19 17:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found