Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 677
  • Last Modified:

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

0
TheOhmit
Asked:
TheOhmit
  • 8
  • 6
  • 5
3 Solutions
 
Ray PaseurCommented:
The advice to use CURL to download files is technically incompetent unless Network Solutions can show you an example of how it is expected to work.  But don't feel bad -- lots of people have chosen GoDaddy or NetSol with equally unsatisfactory customer "support" experiences.

Here is a script that will force a download of almost any file.  Install it and try it out.  Please post back with any questions. ~Ray
<?php // RAY_force_download_GET.php
error_reporting(E_ALL);

// EXAMPLE OF USAGE
// <a target="_blank"
//    href="RAY_force_download_GET.php?u=http://www.google.com/intl/en_ALL/images/logo.gif">Google Logo</a>



// THE NAME OF THE FILE TO DOWNLOAD IS IN THE URL
$url = $_GET["u"];

// USE CASE
force_download($url);



// FUNCTION TO FORCE A DOWNLOAD FROM A FILE
function force_download($filename)
{
    // TRY TO GET THE CONTENTS OF THE FILE
    $filedata = @file_get_contents($filename);

    // SUCCESS
    if ($filedata)
    {
        // GET A NAME FOR THE FILE
        $basename = basename($filename);

        // THESE HEADERS ARE USED ON ALL BROWSERS
        header("Content-Type: application-x/force-download");
        header("Content-Disposition: attachment; filename=\"$basename\"");
        header("Content-length: ".(string)(strlen($filedata)));
        header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");
        header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");

        // THIS HEADER MUST BE OMITTED FOR IE 6+
        if (FALSE === strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE '))
        {
            header("Cache-Control: no-cache, must-revalidate");
        }

        // THIS IS THE LAST HEADER
        header("Pragma: no-cache");

        // FLUSH THE HEADERS TO THE BROWSER
        flush();

        // CAPTURE THE FILE IN THE OUTPUT BUFFERS - WILL BE FLUSHED AT SCRIPT END
        ob_start();
        echo $filedata;
    }

    // FAILURE
    else
    {
        die("ERROR: UNABLE TO OPEN $filename");
    }
}

Open in new window

0
 
TheOhmitAuthor Commented:
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?
0
 
Ray PaseurCommented:
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.
0
Get 10% Off Your First Squarespace Website

Ready to showcase your work, publish content or promote your business online? With Squarespace’s award-winning templates and 24/7 customer service, getting started is simple. Head to Squarespace.com and use offer code ‘EXPERTS’ to get 10% off your first purchase.

 
TheOhmitAuthor Commented:
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
0
 
TheOhmitAuthor Commented:
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.
0
 
Ray PaseurCommented:
0
 
onemadeyeCommented:
I bet the host do not allowed file_get_contents to be executed and that's why they ask you to use cURL ...
Well, you can try to replace file_get_contents (from Ray's PHP file above) with this function from http://snipplr.com/view.php?codeview&id=4084

Goes like this :
<?php // RAY_force_download_GET.php
error_reporting(E_ALL);

// EXAMPLE OF USAGE
// <a target="_blank"
//    href="RAY_force_download_GET.php?u=http://www.google.com/intl/en_ALL/images/logo.gif">Google Logo</a>

// THE NAME OF THE FILE TO DOWNLOAD IS IN THE URL
$url = $_GET["u"];

// USE CASE
force_download($url);

function file_get_contents_curl($url) {
	$ch = curl_init();
	
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //Set curl to return the data instead of printing it to the browser.
	curl_setopt($ch, CURLOPT_URL, $url);
	
	$data = curl_exec($ch);
	curl_close($ch);
	
	return $data;
}

// FUNCTION TO FORCE A DOWNLOAD FROM A FILE
function force_download($filename)
{
    // TRY TO GET THE CONTENTS OF THE FILE
    $filedata = file_get_contents_curl($filename);

/// rest of the codes here

Open in new window

0
 
Ray PaseurCommented:
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

0
 
TheOhmitAuthor Commented:
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

0
 
Ray PaseurCommented:
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.
0
 
TheOhmitAuthor Commented:
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?
0
 
Ray PaseurCommented:
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.
0
 
onemadeyeCommented:
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

0
 
onemadeyeCommented:
Or straight to a bigger value :
<?php
ini_set("max_execution_time","600");
ini_set("memory_limit","128M"); 

Open in new window

0
 
onemadeyeCommented:
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...
0
 
TheOhmitAuthor Commented:
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.
0
 
TheOhmitAuthor Commented:
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?
0
 
onemadeyeCommented:
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 :-)
0
 
TheOhmitAuthor Commented:
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.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Easily Design & Build Your Next Website

Squarespace’s all-in-one platform gives you everything you need to express yourself creatively online, whether it is with a domain, website, or online store. Get started with your free trial today, and when ready, take 10% off your first purchase with offer code 'EXPERTS'.

  • 8
  • 6
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now