in reply to Creating downloadable files on the fly

Are you sending the correct Content-Type header to tell the browser that this is an excel file? The Excel content type is:
Content-Type: application/vnd.ms-excel
To force a file download, use the Content-Disposition header:
Content-Disposition: attachment; filename=test.xls
Basically print these two lines, followed by a blank line, then send the Excel data.

JJ

Replies are listed 'Best First'.
Re: Re: Creating downloadable files on the fly
by surfmonkey (Acolyte) on Oct 09, 2002 at 10:46 UTC
    Yeah, I either print those two Content headers or set them using the Response object, but they seem to have no effect. It seems to write the excel stream before it writes the header info so I get the following (source dump of resultant page):
    ÐÏà¡....ÿÿÿÿÿÿÿÿÿÿÿ <--loads of stuff HTTP/1.1 200 OK Date: Wed, 09 Oct 2002 10:43:16 GMT Server: Apache/1.3.20 (Win32) mod_ssl/2.8.4 OpenSSL/0.9.6b mod_perl/1. +26 ContentDisposition: attachment; filename=test.xls Content-Length: 0 Expires: Wed, 09 Oct 2002 10:43:15 GMT Cache-Control: private Keep-Alive: timeout=15, max=98 Connection: Keep-Alive Content-Type: application/vnd.ms-excel
      I've knocked up a quick test:
      #!/usr/bin/perl -w package Apache::ExcelTest; use Apache::Constants qw(:common); use Apache::Request; use Spreadsheet::WriteExcel; sub handler{ my $r = Apache::Request->new(shift); print "Content-disposition: attachment;filename=test.xls\n"; print "Content-type: application/vnd.ms-excel\n\n"; tie *XLS => 'Apache'; binmode(*XLS); my $workbook = Spreadsheet::WriteExcel->new(\*XLS); my $worksheet = $workbook->addworksheet(); $worksheet->write("A1","Hello, World"); $workbook->close; } 1;
      and here is the Apache config to go with it:
      <Location /exceltest> SetHandler perl-script PerlHandler Apache::ExcelTest PerlSendHeader on </Location>
      To see it in action, go to http://jonallen.info/exceltest.

      Hope this points you in the right direction.

      JJ

        I was playing with writing a spreadsheet to the screen, and this code works:
        #!/usr/local/bin/perl use strict; use warnings; use CGI qw/:standard/; if( param()) { print header(-type=>'application/vnd.ms-excel', -attachment=>'test.xls'); use Spreadsheet::WriteExcel; my $workbook = Spreadsheet::WriteExcel->new('-'); my $worksheet = $workbook->addworksheet(param('color')); $worksheet->write(0,0,param('name')); $worksheet->write(0,1,"dere"); $worksheet->write(0,2,"be jammin"); $worksheet->write(1,0,"Monkey"); $worksheet->write(2,0,param('words')); $workbook->close(); } else { print header, start_html('A Simple Example'), h1('A Simple Example'), start_form, "What's your name? ",textfield('name'),p, "What's the combination?", p, checkbox_group(-name=>'words', -values=>['eenie','meenie','minie','moe'], -defaults=>['eenie','minie']), p, "What's your favorite color? ", popup_menu(-name=>'color', -values=>['red','green','blue','chartreuse']),p, submit, end_form, hr; }
        Hope this helps! Update: The above code was pretty much cut'n'pasted from cgi
        I still get exactly the same result. On my local machine the excel data appears in text format. It worked from your URL though. I have checked the MIME headers supported by my browser and vnd-ms.excel is listed:
        GET /HTest HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, applicati +on/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, * +/* Accept-Encoding: gzip, deflate Accept-Language: en-gb Connection: Keep-Alive Host: localhost User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) HTTP/1.1 200 OK Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/plain
        Do I need to install libapreq to get this to work? I haven't yet managed to get it to compile (it tells me I don't have mod_perl installed!)
        OK, I do have mod_perl installed. I have now installed libapreq and the demo works. Thanks. I had to change the makefile and hard code the paths to the includes and libs MyConfig wasn't picking them up!!. So now I can generate Excel files on the fly from a handler, but I don't want to write a new handler for every file. How do I pass values onto the handler so that it can acces the correct database and dataset? I've never played with handlers before so this is completely new territory for me.

      When you say Response object, I assume you write the app in Perlscript, right or ... Anyway below is a snippet c&p'ed from working code

      ## previous code $Response->{'Buffer'} = 1; $Response->{ContentType} = "application/vnd.ms-excel"; $Response->AddHeader('Content-Disposition', "attachment; filename=$fil +ename"); # 'Specify size so that the browser's progress bar works properly $Response->AddHeader( "Content-Length", -s $long_filename); open(IN, "<$long_filename") || AppendToLog("Unable to open yada yada " +); binmode(IN); my @buf = <IN>; close(IN); BinaryWrite(@buf); $Response->Flush; ## more code sub BinaryWrite(@) { my $output; foreach $output (@_) { if (length($output) > 128000) { BinaryWrite(unpack('a128000 a*', $output)); } else { my $variant =Win32::OLE::Variant->new(VT_UI1, $output); $Response->BinaryWrite($variant); } } }

      Please also remember that things will fail if you output anything to STDOUT before

        Thanks for that, I was hoping to create them on the fly and not have to store them in files prior to the user downloading them. But if I can't get it to work that is what I will have to do. Could get quite disc hungry, some of the data sets are many, many MBs in size.
      Can you reduce this to a test case (i.e. use a very simple Excel file such as
      my $workbook = Spreadsheet::WriteExcel->new(\*.XLS); my $worksheet = $workbook->addworksheet(); $worksheet->write("A1","Hello, World");
      and then post your complete script. I'll then run it on my machine and see if I can reproduce the problem.

      JJ