Solved

cURL Solution to replace getfile.php

Posted on 2011-02-16
19
602 Views
Last Modified: 2012-05-11
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
Comment
Question by:TheOhmit
  • 8
  • 6
  • 5
19 Comments
 
LVL 108

Accepted Solution

by:
Ray Paseur earned 300 total points
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
0
 
LVL 5

Assisted Solution

by:onemadeye
onemadeye earned 200 total points
Comment Utility
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
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
 
LVL 5

Expert Comment

by:onemadeye
Comment Utility
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
 
LVL 5

Assisted Solution

by:onemadeye
onemadeye earned 200 total points
Comment Utility
Or straight to a bigger value :
<?php
ini_set("max_execution_time","600");
ini_set("memory_limit","128M"); 

Open in new window

0
 
LVL 5

Expert Comment

by:onemadeye
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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
 
LVL 5

Expert Comment

by:onemadeye
Comment Utility
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
 

Author Comment

by:TheOhmit
Comment Utility
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

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Envision that you are chipping away at another e-business site with a team of pundit developers and designers. Everything seems, by all accounts, to be going easily.
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
This tutorial demonstrates how to identify and create boundary or building outlines in Google Maps. In this example, I outline the boundaries of an enclosed skatepark within a community park.  Login to your Google Account, then  Google for "Google M…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now