Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I provide a temp file download using mod_perl and CGI::Application?

My web app runs on Apache mod_perl using CGI::Application. I want to provide a download of a generated file. In the past (before we were using mod_perl and CGI::App) I just spooled out a csv file to STDOUT as it was generated. Now I'm shooting for a little more refinement - creating an Excel spreadsheet using Spreadsheet::WriteExcel - and I can't seem it to get to print it out from the file handle.

sub export_list {
  my $self = shift;

  binmode(STDOUT);
  my $str;
  open my $fh, '>', \$str;
  my $workbook = Spreadsheet::WriteExcel->new($fh);
  my $worksheet = $workbook->add_worksheet();

  $worksheet->write_col(0,0, ['some','data','here']);
  warn $str;
  return $str;
}

The output is just a blank response, and the warn is blank as well.

The method I'm using to write the spreadsheet to a filehandle is pretty much straight out of the documentation, so I assume the problem is due to some CGI::App noobery on my part. The documentation's suggested methods for filehandles and mod_perl proved pretty fruitless as well.

I guess I should mention I'm running on Windows, and that my current workaround is to create a file and provide the user with a link to it. That poses more problems, however, in regards to clearing out the directory and when to do so, and also authentication for access to the generated files.

Suggestions? Scathing criticism?

like image 489
wes Avatar asked Aug 28 '09 20:08

wes


2 Answers

You shouldn't need to mess with STDOUT; CGI-App should handle that properly for you under the hood. You'll also may need to close the filehandle before you try to send the data.

It looks like you're not setting a proper content type for the Excel data, though. For anything other than text/html, you'll need to set it manually. Try something like this:

sub export_list {
    my $self = shift;

    my $str;
    open my $fh, '>', \$str or die "Can't open to var: $!";
    my $workbook = Spreadsheet::WriteExcel->new($fh);
    my $worksheet = $workbook->add_worksheet();

    $worksheet->write_col(0,0, ['some','data','here']);

    $workbook->close;
    close $fh;

    warn $str;

    $self->header_add( -type => 'application/vnd.ms-excel' );
    return $str;

}

You may also be interested in CGI::Application::Plugin::Stream

like image 158
friedo Avatar answered Oct 22 '22 13:10

friedo


Instead of creating the whole spreadsheet in memory, you should either write it out to a file and them stream it when finished (using CGI::Application::Plugin::Stream helps here, but you'd still need to clean it up afterwards, but really every web app should have a temp directory that periodically gets cleaned up) or print it as you create it (which means making the FH STDIN instead which might be trickier under mod_perl or maybe not).

And then remember to close your workbook when it's done.

like image 34
mpeters Avatar answered Oct 22 '22 13:10

mpeters