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

Well, monks

I know how to generate HTML code with CGI for mod-perl and Apache.

But I would like to send a file (image, zip,etc..) as if Apache was sending it directly, but only when my perl script checks if it is allowed. that is instead of img src="www.ttt.ccom/image.gif" my script be called such as img src="www.ttt.ccom/send.pl?image.gif" Is there a module, or a tutorial or a code snippet somewhere for sending a file?

Thank you

Pierre Couderc

Replies are listed 'Best First'.
Re: How to send a file vith apache?
by matija (Priest) on May 01, 2004 at 09:22 UTC
    It's really quite simple:
    if (can_send($file)) { # we assume you can program whatever rules you +need here open(INP,"<$file") || die "Can not open $file:$!\n"; # but handle t +hat in can_send my MIME::Types $types = MIME::Types->new; my $mime = $types->mimeTypeOf($file) || "application/octet-stream"; print header("Content-type: $mime"); binmode(INP); binmode(STDOUT); while (<INP>) { print $_; } close(INP); }

      That works, but I don't like the use of the <> diamond operator to deal with binary files. If there aren't any newline characters in the file, you'll read it all in one go; if there are many of them you'll read a line and print it many times.

      I think it's better to use the simple read function for this:

      while (read(INP,$buf,8192)) { print $buf; }

      Much more predictable behavior.

        Well, if you use sysread, you should also use syswrite instead of print. That way you don't have to bother with binmode, etc. And somehow, it seems cleaner to me than mixing the two different idioms.
Re: How to send a file vith apache?
by adamk (Chaplain) on May 02, 2004 at 00:24 UTC
    I've had troubles from time to time with the way various browsers work with various headers, particularly the difference between Netscape/Mozilla and IE.

    Several years ago, we finally managed to stumble on a header combination that "Just Works" properly for all known versions of all known browsers, and WILL save the file with the filename that you want.

    CGI::header( -type => "application/octet-stream; name=$filename", '-Content-Disposition' => "attachment; filename=$filename", '-Content-Transfer-Encoding' => 'binary', );

    It's served me well for many years now.
    Enjoy!
Re: How to send a file vith apache?
by strat (Canon) on May 02, 2004 at 08:12 UTC
    With mod_perl and Apache::File, this is rather easy:
    use Apache::Constants qw(:common); use Apache::Request; use Apache::File; sub handler { my $r = Apache::Request->new(shift); my $serverFile = $r->param('file'); # needs some errorhandling here if $serverFile is not # submitted, invalid paths, ... # get $contenttype somehow; I always save it in a # database when I upload the file my $fh = Apache::File->new($serverFile); binmode($fh); unless ($fh) { $r->log_error("Can't open '$serverFile': $!"); } # unless else { $r->send_http_header($contenttype); $r->send_fd($fh); } # else return OK; } # handler

    well, you'll need to do some errorhandling...

    If you use a temporary filename at the server, it is not so easy to tell the client to save the file under the original filename. I like to use mod_rewrite (httpd.conf) or PerlTransHandler to prevent this and which could look like:

    RewriteEngine on RewriteRule /download/(.+)/(.*) /perl-bin/download.pl?file=$1;$2

    There $1 is the temporary filename and $2 the original filename. Or with PerlTransHandler:

    package TransHandler; use warnings; use strict; use Apache::Constants qw(DECLINED); sub handler { my $r = shift; my $standardUri = "/pboard/PBoard.pm"; my $uri = $r->uri(); if ($uri =~ m|^/download/([^/]+)/(.+)|) { $r->uri($standardUri); $r->args("action=download2;file=$1"); return DECLINED; } # elsif return DECLINED; } # handler

    Best regards,
    perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

Re: How to send a file vith apache?
by pcouderc (Monk) on May 01, 2004 at 16:22 UTC
    Thank you very much for your answers : sure it is simple now...