Downloading PDF files

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
SBukoski
Forum Contributor
Posts: 128
Joined: Wed May 21, 2003 10:39 pm
Location: Worcester, MA

Downloading PDF files

Post by SBukoski »

I have a PHP file with the following code defined for downloading files:

Code: Select all

<?php
/*
  $Id: download.php,v 1.9 2003/02/13 03:01:48 hpdl Exp $

  osCommerce, Open Source E-Commerce Solutions
  http://www.oscommerce.com

  Copyright (c) 2003 osCommerce

  Released under the GNU General Public License
*/

  include('includes/application_top.php');

  if (!tep_session_is_registered('customer_id')) die;

// Check download.php was called with proper GET parameters
  if ((isset($HTTP_GET_VARS&#1111;'order']) && !is_numeric($HTTP_GET_VARS&#1111;'order'])) || (isset($HTTP_GET_VARS&#1111;'id']) && !is_numeric($HTTP_GET_VARS&#1111;'id'])) ) &#123;
    die;
  &#125;
  
// Check that order_id, customer_id and filename match
  $downloads_query = tep_db_query("select date_format(o.date_purchased, '%Y-%m-%d') as date_purchased_day, opd.download_maxdays, opd.download_count, opd.download_maxdays, opd.orders_products_filename from " . TABLE_ORDERS . " o, " . TABLE_ORDERS_PRODUCTS . " op, " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " opd where o.customers_id = '" . $customer_id . "' and o.orders_id = '" . (int)$HTTP_GET_VARS&#1111;'order'] . "' and o.orders_id = op.orders_id and op.orders_products_id = opd.orders_products_id and opd.orders_products_download_id = '" . (int)$HTTP_GET_VARS&#1111;'id'] . "' and opd.orders_products_filename != ''");
  if (!tep_db_num_rows($downloads_query)) die;
  $downloads = tep_db_fetch_array($downloads_query);
// MySQL 3.22 does not have INTERVAL
  list($dt_year, $dt_month, $dt_day) = explode('-', $downloads&#1111;'date_purchased_day']);
  $download_timestamp = mktime(23, 59, 59, $dt_month, $dt_day + $downloads&#1111;'download_maxdays'], $dt_year);

// Die if time expired (maxdays = 0 means no time limit)
  if (($downloads&#1111;'download_maxdays'] != 0) && ($download_timestamp <= time())) die;
// Die if remaining count is <=0
  if ($downloads&#1111;'download_count'] <= 0) die;
// Die if file is not there
  if (!file_exists(DIR_FS_DOWNLOAD . $downloads&#1111;'orders_products_filename'])) die;
  
// Now decrement counter
  tep_db_query("update " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " set download_count = download_count-1 where orders_products_download_id = '" . (int)$HTTP_GET_VARS&#1111;'id'] . "'");

// Returns a random name, 16 to 20 characters long
// There are more than 10^28 combinations
// The directory is "hidden", i.e. starts with '.'
function tep_random_name()
&#123;
  $letters = 'abcdefghijklmnopqrstuvwxyz';
  $dirname = '.';
  $length = floor(tep_rand(16,20));
  for ($i = 1; $i <= $length; $i++) &#123;
   $q = floor(tep_rand(1,26));
   $dirname .= $letters&#1111;$q];
  &#125;
  return $dirname;
&#125;

// Unlinks all subdirectories and files in $dir
// Works only on one subdir level, will not recurse
function tep_unlink_temp_dir($dir)
&#123;
  $h1 = opendir($dir);
  while ($subdir = readdir($h1)) &#123;
// Ignore non directories
    if (!is_dir($dir . $subdir)) continue;
// Ignore . and .. and CVS
    if ($subdir == '.' || $subdir == '..' || $subdir == 'CVS') continue;
// Loop and unlink files in subdirectory
    $h2 = opendir($dir . $subdir);
    while ($file = readdir($h2)) &#123;
      if ($file == '.' || $file == '..') continue;
      @unlink($dir . $subdir . '/' . $file);
    &#125;
    closedir($h2); 
    @rmdir($dir . $subdir);
  &#125;
  closedir($h1);
&#125;


// Now send the file with header() magic
  header("Expires: Mon, 26 Nov 1962 00:00:00 GMT");
  header("Last-Modified: " . gmdate("D,d M Y H:i:s") . " GMT");
  header("Cache-Control: no-cache, must-revalidate");
  header("Pragma: no-cache");
  header("Content-Type: Application/octet-stream");
  header("Content-disposition: attachment; filename=" . $downloads&#1111;'orders_products_filename']);

  if (DOWNLOAD_BY_REDIRECT == 'true') &#123;
// This will work only on Unix/Linux hosts
    tep_unlink_temp_dir(DIR_FS_DOWNLOAD_PUBLIC);
    $tempdir = tep_random_name();
    umask(0000);
    mkdir(DIR_FS_DOWNLOAD_PUBLIC . $tempdir, 0777);
    symlink(DIR_FS_DOWNLOAD . $downloads&#1111;'orders_products_filename'], DIR_FS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads&#1111;'orders_products_filename']);
    tep_redirect(DIR_WS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads&#1111;'orders_products_filename']);
  &#125; else &#123;
// This will work on all systems, but will need considerable resources
// We could also loop with fread($fp, 4096) to save memory
    readfile(DIR_FS_DOWNLOAD . $downloads&#1111;'orders_products_filename']);
  &#125;
?>
What I'd like to know is, for PDF's only, how can I make this so it asks the user if they want to open or save it? It currently always opens it in the current window, and I don't want that.

This same file is used for all downloads (zip, exe, pdf, etc) so we have to keep that in mind.

Any suggestions?
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

As far as I know whether a downloaded file opens up in the browser or a prompt comes up to save is determined by the file type and the browser. I think most browsers will always prompt a save as if it's a zip or some other archive type...
SBukoski
Forum Contributor
Posts: 128
Joined: Wed May 21, 2003 10:39 pm
Location: Worcester, MA

Post by SBukoski »

Correct. We don't want to zip the PDF file however. We'd rather present them as PDFs and have the browser offer up the option to save or open.
User avatar
n00b Saibot
DevNet Resident
Posts: 1452
Joined: Fri Dec 24, 2004 2:59 am
Location: Lucknow, UP, India
Contact:

Post by n00b Saibot »

The thing is this that if the particular file-extension is a "known" one, i.e. if an application is registered to handle that extension, in this case Adobe PDF viewer, then it will always open in browser.
SBukoski
Forum Contributor
Posts: 128
Joined: Wed May 21, 2003 10:39 pm
Location: Worcester, MA

Post by SBukoski »

So essentially, other than zipping it, there's no way to force it to download? What if I lie about the file extension and call it a PDT or something like that. It won't know what it is and will therefore always prompt to download. Would that work?
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

You will have to have the user select whether they want to download or open files prior to the download commencing and then change the passthru accordingly.
Don't trust this OsCommerce code you've posted, it's horrendous, complicated and silly.

Check the manual for fpassthru and look especially at the user notes, particularly the one by "axx at axxess dot ca" dated 15-Feb-2004 09:04.
http://php.net/fpassthru wrote: <?
/*/
Download a file using fpassthru()
/*/
$fileDir = "/home/pathto/myfiles"; // supply a path name.
$fileName = "myfile.zip"; // supply a file name.
$fileString=$fileDir.'/'.$fileName; // combine the path and file
// translate file name properly for Internet Explorer.
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")){
$fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
}
// make sure the file exists before sending headers
if(!$fdl=@fopen($fileString,'r')){
die("Cannot Open File!");
} else {
header("Cache-Control: ");// leave blank to avoid IE errors
header("Pragma: ");// leave blank to avoid IE errors
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename="".$fileName.""");
header("Content-length:".(string)(filesize($fileString)));
sleep(1);
fpassthru($fdl);
}
?>
Most importantly: read up on MIME types.
User avatar
n00b Saibot
DevNet Resident
Posts: 1452
Joined: Fri Dec 24, 2004 2:59 am
Location: Lucknow, UP, India
Contact:

Post by n00b Saibot »

if you don't send an "Content-Type: XXX" Header and append any arbitrary ext like report.pdf.xxx then it will always prompt to save. ;)
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

n00b Saibot wrote:if you don't send an "Content-Type: XXX" Header and append any arbitrary ext like report.pdf.xxx then it will always prompt to save. ;)
IE has a notoriously buggy implementation of HTTP headers, I'd warn against not defining the MIME type. Generic MIME-types like "octet" will prompt the user for action.
User avatar
n00b Saibot
DevNet Resident
Posts: 1452
Joined: Fri Dec 24, 2004 2:59 am
Location: Lucknow, UP, India
Contact:

Post by n00b Saibot »

patrikG wrote:I'd warn against not defining the MIME type.
isn't that what i referred to when I said in my post
I wrote:if you don't send an "Content-Type: XXX" Header
If i am wrong , please correct me. :)
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

no. You said not to specify the MIME type. I say you must.
User avatar
n00b Saibot
DevNet Resident
Posts: 1452
Joined: Fri Dec 24, 2004 2:59 am
Location: Lucknow, UP, India
Contact:

Post by n00b Saibot »

patrikG wrote:no. You said not to specify the MIME type. I say you must.
had misunder...
At first you wrote:I'd warn against not defining the MIME type
what happens if i don't send MIME type and IE reads it :?:
jus' improving my inpho :lol:
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

usually, a correctly configured browser will ask you what you want to do with the file... and if you configure your browser to launch a program when content with a given mimetype arrives... it is the user that has decided this..

websites should not try to work-around this. teach your users how to use a webbrowser. not the other way round.
Post Reply