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

I'm having some trouble with some CGI/Perl code on my webserver. The following function uses the Archive::zip module to zip a directory on the fly and send it to STDOUT when the client clicks on a certain link. It zips the file correctly but I think I have something wrong with the headers because when the download dialog box pops up it doesn't have a filename to save and it thinks the MIME type is "httpd/unix directory" (at least in Firefox). It then saves it as a randomly generated filename without an extension. Any ideas as to how to send the headers correctly so it knows it's a zip file and uses the filename I specify?
sub fetch_archive { my $path = "path/to/file/"; my $filename = "data.zip"; my $zip = Archive::Zip->new(); $zip->addTree( $path, '' ); print "Content-type: text/plain\n" . "Content-Disposition: attachment;$filename\n\n". $zip->writeToFileHandle( 'STDOUT', 0 ); return Apache::OK; }

Replies are listed 'Best First'.
Re: Sending a file through STDOUT using HTTP
by TedYoung (Deacon) on Feb 17, 2005 at 21:53 UTC

    You need to specify the content type as application/zip:

    print "Content-type: application/zip\n" . "Content-Disposition: attachment;$filename\n\n";

    Ted Young

    ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
      That's what I thought too. I guess I should've included the fact that I've tried several different MIME types but that never seems to affect what the browser thinks it is. No matter what headers I put in there it always just thinks it's an "httpd/unix-directory" with no filename.

        Just noticed this too. Change your content-disposition line as follows:

        "Content-Disposition: attachment; filename=\"$filename\"\n\n";

        Ted Young

        ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
Re: Sending a file through STDOUT using HTTP
by Errto (Vicar) on Feb 17, 2005 at 23:59 UTC
    The correct way to set the content type and disposition is (untested)
    print "Content-type: archive/zip\n" . "Content-Disposition: attachment; filename=$filename\n\n"; binmode STDOUT; $zip->writeToFileHandle( \*STDOUT, 0 );
    There's a few points here:
    1. The correct way to set the content-disposition is with the filename= part but without the quotes around the filename (I think).
    2. With your current code you are trying to append the result of writeToFileHandle to your string. I think you mean ";" instead of "."
    3. writeToFileHandle expects a filehandle reference, not a string corresponding to the name of a filehandle.
    4. You should set binmode on a filehandle before writing binary data to it.
    Give that a shot.

      Good points! But, I did have to use the quotes around the file name in the past to handle files with spaces in their names.

      Ted Young

      ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
      Excellent points and I definitely appreciate the info. I now have:
      $zip->addTree( $path, '' ); print "Content-type: application/zip\n" . "Content-Disposition: attachment;filename=\"$filename\"\n\n"; binmode STDOUT; $zip->writeToFileHandle( \*STDOUT, 0 );
      Unfortunately the browser is still seeing it as a "httpd/unix-directory" and not getting a filename. I tried both with and without quotes on the filename. Any other ideas? Stupid MIME types...
        Sorry, forgot to login. The above post was me.
        I did a quick search on Google and it looks like the "httpd/unix-directory" think might be an Apache configuration issue. Are you sure your CGI code is actually getting executed? Maybe add something like print STDERR "My CGI called here" and see if that message shows up in your Apache error log.
Re: Sending a file through STDOUT using HTTP
by TedYoung (Deacon) on Feb 19, 2005 at 15:04 UTC

    Try it on a couple of other browsers... perhapse it is a bug in your version.

    Ted Young

    ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)