Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

cURL Solution to replace getfile.php

Posted on 2011-02-16
19
Medium Priority
?
651 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 6
  • 5
19 Comments
 
LVL 111

Accepted Solution

by:
Ray Paseur earned 1200 total points
ID: 34912356
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
ID: 34912621
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 111

Expert Comment

by:Ray Paseur
ID: 34915866
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:TheOhmit
ID: 34917856
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
ID: 34917955
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 111

Expert Comment

by:Ray Paseur
ID: 34919076
0
 
LVL 5

Assisted Solution

by:onemadeye
onemadeye earned 800 total points
ID: 34920994
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 111

Expert Comment

by:Ray Paseur
ID: 34921267
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
ID: 34922691
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
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 34925202
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
ID: 34954657
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 111

Expert Comment

by:Ray Paseur
ID: 34954691
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
ID: 34956876
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 800 total points
ID: 34956883
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
ID: 34956906
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
ID: 34991462
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
ID: 35010579
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
ID: 35012188
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
ID: 35012462
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

Survive A High-Traffic Event with Percona

Your application or website rely on your database to deliver information about products and services to your customers. You can’t afford to have your database lose performance, lose availability or become unresponsive – even for just a few minutes.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Does your audience prefer people in photos or no people? How can you best highlight what you’re selling? What are your competitors doing, and what can you do that is different and unique from them?  Continue reading to learn how to make your images …
When the s#!t hits the fan, you don’t have time to look up who’s on call, draft emails, call collaborators, or send text messages. An instant chat window is definitely the way to go, especially one like HipChat. HipChat is a true business app. An…
The viewer will learn how to dynamically set the form action using jQuery.
The is a quite short video tutorial. In this video, I'm going to show you how to create self-host WordPress blog with free hosting service.

722 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