http://www.perlmonks.org?node_id=342219

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

Before we continue, I just want you to know that by "force download" I mean the DOWNLOAD and SAVE as box appears, not upload files to their computer without them knowing.

I have a directory of files containing nearly all types of files (sounds, images, text, html, exes) and I want to force a download for that link when they click on the link for that file. I've been told 'application/octet-stream' is how you force ANY file to download rather than load in the screen (like images typically load to screen, right?).

The link forces a download right now, but it tries to download the CGI script itself without the extension instead of the file I'm telling it to. It's requesting action on a file "filemanager" instead of let's say image.gif or sound.wav. Does anyone see where I went wrong? I am linking to in like <a href="filemanager.cgi?dl=$_">download!</a> and $_ does indeed contain what I expect it to. I've done testing to be sure my variable contains what it should, and it does.

Where did I go wrong?

# $dl_file is absolute path to uploaded directory where files are kept my $download = url_param('dl'); if ($download ne "") { if (exists $uploadedfiles{$download}) { my($file, $type, $time, $size) = split(/::/, $uploadedfiles{$down +load}); my $load_file = join("", $dl_file, $file); print header(-type=>'application/octet-stream', -attachment=>"$load_file", -expires=>'-1d'); print start_html); print end_html; exit; } else { print header, start_html("ERROR"); print qq(<center>Does not exist!</center>); print end_html; exit; } }

Replies are listed 'Best First'.
Re: Force download -- Repost
by Kozz (Friar) on Apr 03, 2004 at 03:27 UTC
    Your main problem is that you're only outputting the header and not the contents of the file itself. If you're going to output your own mime-type header like this, you would then need to open the file in question (use binmode()) in chunks, outputting those chunks to the browser as they are read.
    my $buffer; open(DOWNLOAD, "< /path/requested_file") or die "Could not read file: +$!"; binmode(DOWNLOAD); while( read(DOWNLOAD, $buffer, 4096) ){ print $buffer; } close(DOWNLOAD);
    "Forcing a download" is really a function of the browser, and results from the browser's decision on how to handle the particuar mime type you send. You may want to also try "application/binary" in addition to the "application/octet-stream". In your code, I think that using 'join' is a bit of overkill when you can concatenate the strings as
    my $load_file = $dl_file . $file;
    Also, I would get rid of the start_html and end_html bit -- unnecessary, and I suppose could mess up your headers, too.
      Thank you for your suggestions but it's still producing the same output as before. It enforces a DOWNLOAD/SAVE AS popup without the filename (but it pops up with the script name without the extension).

      I added your buffer and print codes and commented out the HTML headers. It didn't error out so I assume it could find the file, it's just not producing ANYTHING other than a popup without a file.

      Any other suggestions?

Re: Force download -- Repost
by sgifford (Prior) on Apr 03, 2004 at 08:53 UTC
    Node 61642 seems to have nearly the same question; perhaps some of the ideas there will work for you?
•Re: Force download -- Repost
by merlyn (Sage) on Apr 03, 2004 at 10:48 UTC
    Please, give up the notion that you can "force" anything on the web. Instead, provide your link, have the proper MIME type, and let me steer. Don't take over the wheel for me. I will resent it and either not visit your site again, or do it only with regret.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

    A reply falls below the community's threshold of quality. You may see it by logging in.