Guest Posted August 2, 2004 Posted August 2, 2004 (edited) I have tried to make the download function to work properly. It took me a couple of hours by try and error. I would like to share my experience and hope it is useful to people have the similar problem. My original problem was, when user click on the download link, the downloaded file is checkout_success.php or the download.php. It is not the file that I give in the downlaod database. If the server does not allow to downlaod these files, the download will fail. Optionally, user can right click on the link and choose the "Save Target As" menu. But the file name appear in the save dialogue is confusing, it is "checkout_success.php" or "download", not the right file extension. If user does not give the correct file extension to save the file, the downloaded file will not be able to run. I found the following problems: 1. If user uses Windows and Internet Explorer, the "attachment" should not appear in the header ""Content-disposition: attachment; filename=..". But it is required by other type of browsers. 2. The download file I give is not a plain file name. It is combined with a directary. I have to use diferent directory to store a huge number of download files, insted of putting them in a flat directory. But in the "Content-disposition: attachment; filename=.." header, the filename nust be a plain file name. I added the file size so that user can expect how long the download will take. Because my files are binaries, I use binary transfer encoding. I also added "force-download" because I want user to get an empty file instead of an error file. What follows is the the code that I modifed in the download.php: <?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['order']) && !is_numeric($HTTP_GET_VARS['order'])) || (isset($HTTP_GET_VARS['id']) && !is_numeric($HTTP_GET_VARS['id'])) ) { ? ?die; ?} ? // 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['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['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['date_purchased_day']); ?$download_timestamp = mktime(23, 59, 59, $dt_month, $dt_day + $downloads['download_maxdays'], $dt_year); // Die if time expired (maxdays = 0 means no time limit) ?if (($downloads['download_maxdays'] != 0) && ($download_timestamp <= time())) die; // Die if remaining count is <=0 ?if ($downloads['download_count'] <= 0) die; // Die if file is not there ?if (!file_exists(DIR_FS_DOWNLOAD . $downloads['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['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() { ?$letters = 'abcdefghijklmnopqrstuvwxyz'; ?$dirname = '.'; ?$length = floor(tep_rand(16,20)); ?for ($i = 1; $i <= $length; $i++) { ? $q = floor(tep_rand(1,26)); ? $dirname .= $letters[$q]; ?} ?return $dirname; } // Unlinks all subdirectories and files in $dir // Works only on one subdir level, will not recurse function tep_unlink_temp_dir($dir) { ?$h1 = opendir($dir); ?while ($subdir = readdir($h1)) { // 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)) { ? ? ?if ($file == '.' || $file == '..') continue; ? ? ?@unlink($dir . $subdir . '/' . $file); ? ?} ? ?closedir($h2); ? ?@rmdir($dir . $subdir); ?} ?closedir($h1); } // 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['orders_products_filename']); */ ?$filepath = DIR_FS_DOWNLOAD . $downloads['orders_products_filename']; ?$arr = explode('/',$downloads['orders_products_filename']); ?$filename = end($arr); ?$size = filesize ("$filepath"); ?header("Pragma: public"); ?header("Expires: 0"); ?header("Accept-Ranges: bytes"); ?header("Content-Length: " . $size); ?header("Pragma: no-cache"); ?header("Cache-Control: no-cache, must-revalidate"); ?header("Content-Description: File Transfer"); ?header("Content-Type: application/force-download"); ?header("Content-Transfer-Encoding: binary"); ?$user_agent = strtolower ($_SERVER["HTTP_USER_AGENT"]); if ((is_integer (strpos($user_agent, "msie"))) && (is_integer (strpos($user_agent, "win")))) { ?header( "Content-Disposition: filename=".$filename); } else { ?header( "Content-Disposition: attachment; filename=".$filename); } ?if (DOWNLOAD_BY_REDIRECT == 'true') { // 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['orders_products_filename'], DIR_FS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads['orders_products_filename']); ? ?tep_redirect(DIR_WS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads['orders_products_filename']); ?} else { // 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['orders_products_filename']); ? readfile($filepath); ?} ?> Edited August 2, 2004 by james_times Quote
Guest Posted August 2, 2004 Posted August 2, 2004 (edited) Sorry, I commented the code line under: // Now decrement counter for testing purpose. You should uncomment this line: tep_db_query("update " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . ... in order to decrease the count of download. Edited August 2, 2004 by james_times Quote
Guest Posted August 3, 2004 Posted August 3, 2004 This sounds like a nice little project, as I cannot install the download controller as my site is heavily modded this may be an alternative to a small problem which I have ignored. Welldone James keep up the good work. Quote
grig Posted September 27, 2005 Posted September 27, 2005 (edited) WOW! This seems to work for me! I've tried a bunch of attempts at getting the headers right on here. This seems best so far. Edited September 27, 2005 by grig Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.