Link to home
Start Free TrialLog in
Avatar of TheOhmit
TheOhmitFlag for United States of America

asked on

cURL Solution to replace getfile.php

We use a secondary web host to store/serve downloadable media content for our website.  I just switched this host because of storage space and pricing issues.  We are now using Network Solutions, but the getfile script I was using to download files no longer works.  They have informed that this is by design (although I don't understand it), and that I should use cURL to allow users to download the content.  But, I'm not sure how to do this.  

I have found this page describing how to do what I am trying to do, but I am not sure how to implement it.  I have not dabbled much into programming in PHP.  If anyone could help me figure this out, I would greatly appreciate it.  

Here is my existing getfile.php:

<?php

$filename = $_GET['file'];

// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
  ini_set('zlib.output_compression', 'Off');

// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));
if( $file_extension == "mp4")
 {
if( $filename == "" ) 
{
  echo "<html><title>Download Script</title><body>ERROR: download file NOT SPECIFIED. USE force-download.php?file=filepath</body></html>";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  echo "<html><title>Download Script</title><body>ERROR: File not found. USE force-download.php?file=filepath</body></html>";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "mp4": $ctype="movie/mp4"; break;
  case "mp3": $ctype="audio/mp3"; break;
  case "doc": $ctype="application/doc"; break;
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
}
elseif( $file_extension == "pdf")
 {
if( $filename == "" ) 
{
  echo "<html><title>Download Script</title><body>ERROR: download file NOT SPECIFIED. USE force-download.php?file=filepath</body></html>";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  echo "<html><title>Download Script</title><body>ERROR: File not found. USE force-download.php?file=filepath</body></html>";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "mp4": $ctype="movie/mp4"; break;
  case "mp3": $ctype="audio/mp3"; break;
  case "doc": $ctype="application/doc"; break;
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
}
elseif( $file_extension == "mp3")
 {
if( $filename == "" ) 
{
  echo "<html><title>Download Script</title><body>ERROR: download file NOT SPECIFIED. USE force-download.php?file=filepath</body></html>";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  echo "<html><title>Download Script</title><body>ERROR: File not found. USE force-download.php?file=filepath</body></html>";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "mp4": $ctype="movie/mp4"; break;
  case "mp3": $ctype="audio/mp3"; break;
  case "doc": $ctype="application/doc"; break;
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
}
elseif( $file_extension == "doc")
 {
if( $filename == "" ) 
{
  echo "<html><title>Download Script</title><body>ERROR: download file NOT SPECIFIED. USE force-download.php?file=filepath</body></html>";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  echo "<html><title>Download Script</title><body>ERROR: File not found. USE force-download.php?file=filepath</body></html>";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "mp4": $ctype="movie/mp4"; break;
  case "mp3": $ctype="audio/mp3"; break;
  case "doc": $ctype="application/doc"; break;
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
}
elseif( $file_extension == "php")
 { echo "You are not an 31337 H4X0R!";
}
;
?>

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of TheOhmit

ASKER

Thanks Ray!  I've got it all setup, but am receiving an error:

Notice: Undefined index: u in /data/25/2/156/34/2482849/user/2719045/htdocs/getfile.php on line 11
ERROR: UNABLE TO OPEN

Line 11 is:    $url = $_GET["u"];

The URL I am sending it is:    http://pccmedia.net/getfile.php?url=mp4s/ma1106-lo.mp4

Any ideas?
The "undefined index" message means that on line 11, where you see this line of code...

$url = $_GET["u"];

... there is no value in the $_GET array named "u" -- it appears you've got "url=" instead of "u=" in the test URL.  Change one or the other to achieve consistency between the two strings and you will be fine.
Ah, ok that makes sense.  I have done that and no longer get the error.  The script still does not download the file though.  It just goes to a blank white page.  Upon viewing the source code for that page, it is completely blank.

The script does appear to be working, partly; if I change the filename to a file that doesn't exist, it does return the correct error: "ERROR: UNABLE TO OPEN mp4s/ma1106.mp4".  It just does not download the file when the correct filename is put in:

http://pccmedia.net/getfile.php?url=mp4s/ma1106-lo.mp4
I just found that if I put in the absolute URL, it returns an error rather than downloading the file:

http://pccmedia.net/getfile.php?url=http://www.pccmedia.net/mp3s/ma1106.mp3

http://www.pccmedia.net/mp3s/ma1106.mp3 is a real file and plays in-browser.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Actually, I have found that some site like to test the headers, etc., and this cURL function has proven fairly reliable.  If you can't use file_get_contents() with a URL, this is pretty close to a one-to-one drop in for file_get_contents() with the added advantage that the timeout parameter will give your script control again (as opposed to just blowing up) if the foreign site fails to respond.
<?php // RAY_curl_example.php
error_reporting(E_ALL);

// A FUNCTION TO RUN A CURL-GET CLIENT CALL TO A FOREIGN SERVER
function my_curl
( $url
, $get_array=array()
, $timeout=3
, $error_report=TRUE
)
{
    // PREPARE THE POST STRING
    $get_string = '';
    foreach ($get_array as $key => $val)
    {
        $get_string .= urlencode($key) . '=' . urlencode($val) . '&';
    }
    if (!empty($get_string)) $url .= '?' . $get_string;

    $curl = curl_init();

    // HEADERS AND OPTIONS APPEAR TO BE A FIREFOX BROWSER REFERRED BY GOOGLE
    $header[] = "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
    $header[] = "Cache-Control: max-age=0";
    $header[] = "Connection: keep-alive";
    $header[] = "Keep-Alive: 300";
    $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
    $header[] = "Accept-Language: en-us,en;q=0.5";
    $header[] = "Pragma: "; // BROWSERS USUALLY LEAVE BLANK

    // SET THE CURL OPTIONS - SEE http://php.net/manual/en/function.curl-setopt.php
    curl_setopt( $curl, CURLOPT_URL,            $url  );
    curl_setopt( $curl, CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  );
    curl_setopt( $curl, CURLOPT_HTTPHEADER,     $header  );
    curl_setopt( $curl, CURLOPT_REFERER,        'http://www.google.com'  );
    curl_setopt( $curl, CURLOPT_ENCODING,       'gzip,deflate'  );
    curl_setopt( $curl, CURLOPT_AUTOREFERER,    TRUE  );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER, TRUE  );
    curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, TRUE  );
    curl_setopt( $curl, CURLOPT_TIMEOUT,        $timeout  );

    // RUN THE CURL REQUEST AND GET THE RESULTS
    $htm = curl_exec($curl);

    // ON FAILURE HANDLE ERROR MESSAGE
    if ($htm === FALSE)
    {
        if ($error_report)
        {
            $err = curl_errno($curl);
            $inf = curl_getinfo($curl);
            echo "CURL FAIL: $url TIMEOUT=$timeout, CURL_ERRNO=$err";
            var_dump($inf);
        }
        curl_close($curl);
        return FALSE;
    }

    // ON SUCCESS RETURN XML / HTML STRING
    curl_close($curl);
    return $htm;
}




// USAGE EXAMPLE - PUT YOUR FAVORITE URL HERE
$url = "http://finance.yahoo.com/d/quotes.csv";

// PUT YOUR ARRAY OF KEY=>VALUE PAIRS HERE
$arg = array
( 's' => 'lulu'
, 'f' => 'snl1c1ohgvt1'
)
;

// MAKE THE CALL
$htm = my_curl($url, $arg, 2, TRUE);
if (!$htm) die("NO $url");

// SHOW WHAT WE GOT
echo "<pre>";
var_dump($arg);
echo PHP_EOL . $url;
echo PHP_EOL . htmlentities($htm);
echo PHP_EOL;

// TRY ANOTHER WITHOUT ARGUMENTS
$url = 'http://twitter.com';
$htm = my_curl($url);
echo PHP_EOL . $url;
echo PHP_EOL . htmlentities($htm);
echo PHP_EOL;

Open in new window

I have tried onemadeyes's alteration to the script.  If I send it a relative link in the url I get the same error (unable to open).  If I send it an absolute link in the URL, I get....

Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 16515937 bytes) in /data/25/2/156/34/2482849/user/2719045/htdocs/getfile.php on line 21

Open in new window



I also tried Ray's new script...which is I understand even less than the other one.  I may not have set this one up right.   I left it as-is with the usage example and also tried setting $url = "http://www.pccmedia.net/mp3s/ma1106.mp3", and I receive an even less helpful error (to me).

CURL FAIL: mp3s/ma1106.mp3?s=lulu&f=snl1c1ohgvt1& TIMEOUT=2, CURL_ERRNO=6array(20) { ["url"]=> string(45) "http://mp3s/ma1106.mp3?s=lulu&f=snl1c1ohgvt1&" ["content_type"]=> NULL ["http_code"]=> int(0) ["header_size"]=> int(0) ["request_size"]=> int(0) ["filetime"]=> int(-1) ["ssl_verify_result"]=> int(0) ["redirect_count"]=> int(0) ["total_time"]=> float(0) ["namelookup_time"]=> float(0) ["connect_time"]=> float(0) ["pretransfer_time"]=> float(0) ["size_upload"]=> float(0) ["size_download"]=> float(0) ["speed_download"]=> float(0) ["speed_upload"]=> float(0) ["download_content_length"]=> float(0) ["upload_content_length"]=> float(0) ["starttransfer_time"]=> float(0) ["redirect_time"]=> float(0) } NO mp3s/ma1106.mp3

Open in new window

This explanation is about as clear as you can get: Allowed memory size of 16777216 bytes exhausted.  Your PHP process is running in a 16MB space, and the attempt to load the MP3 file caused it to run out of memory.  I have no idea why you might be running in a 16MB space.  Most modern hosting companies would allocate many times that.

And you might want to look up CURL error number 6!  It says, in essence, that the URL you are trying to access is bogus.
http://lmgtfy.com?q=cURL+Error+6

If you do not know PHP, you might want to hire a professional developer to help you write this application.  Consuming a web service is not an elementary process; there are a lot of antecedent things you would need to know first.  If it's your goal to learn PHP this book will help.
http://www.sitepoint.com/books/phpmysql4/

The CURL example I posted works correctly.  You can test it here (where I tested it before I posted it at EE):
http://www.laprbass.com/RAY_curl_example.php

If you want to post the code as you have it now, we might be able to help, but I really think you would be better off with a professional developer on your team.
Thanks for the help.  I did get the second script to work, but receive the same memory error as the other script.  

Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 16515065 bytes) in /data/25/2/156/34/2482849/user/2719045/htdocs/getfile.php on line 43

Do I have any options besides looking for a different hosting company?
My hosting company is ChiHost.com.  Without asking for any special treatment, I have a memory limit of 128MB.  You might try asking your hosting company to come into the 21st century -- a limit of 16MB is archaically small.
Try to add this at the top of the script:
<?php
ini_set("memory_limit","32M"); 
// Still receive the error then keep adjusting the value

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Or perhaps add this to .htaccess file
php_value upload_max_filesize 100M
php_value max_execution_time 600
php_value post_max_size 100M
php_value max_input_time 100
php_value memory_limit 128M

Open in new window

Check it and let know if it works...
I contacted Network Solutions and they gave me instructions to change the limit in the php.ini file.  I have not had a chance to try it, but will report back in a couple days.
Alright, I'm almost there!

Using Ray's original script with onemadeyes alteration and increasing the memory limit, I can successfully download the files.  I do have a question though.....

The old getfile.php script was almost instantaneous - when clicking on the link it would immediately begin downloading the file.  This script is very slow, and seems to depend on file size.  It takes anywhere from 10 seconds to 2 minutes before it actually begins to download the file.  Is there anything that can be done about that?
I really think the download start time will varies depends on file size ... for example try to download 1GB file and let know how long is the waiting time before it's actually downloading :-)
I agree, there will be some variation.  Some of our video files in the past have been 1GB, but it has never taken more than a few seconds for the download to begin using the other script.