views:

238

answers:

1

The following script is what I usually use to push headers to the browser so that the dialog box appears for users to download a file.

However, in this case the file resides on a different server. I though this should make no difference but it does as when I execute this script with the URL of an externam MP3 file it gives me a "ERROR: File not found". However, this file exists, and I can get to it using the same URL I pass to this script.

Any ideas why? I would appreciate any help.

<?php session_start();

//below variable contains full path to external site where file resides
$filename = $_SESSION['$serverURL'].'audio/'.$_SESSION['fileName'].'.mp3';
//below variable contains a chosen filename of the file to be downloaded
$properFilename = $_GET['properFilename'].'.mp3';

// 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( $filename == "" ) 
{
  //echo "download file NOT SPECIFIED";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  //echo "ERROR: File not found";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "exe": $ctype="application/octet-stream"; break;
  case "zip": $ctype="application/zip"; break;
  case "doc": $ctype="application/msword"; break;
  case "xls": $ctype="application/vnd.ms-excel"; break;
  case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
  case "gif": $ctype="image/gif"; break;
  case "png": $ctype="image/png"; break;
  case "jpeg":
  case "jpg": $ctype="image/jpg"; break;
  default: $ctype="application/force-download";
}
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($properFilename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();   

?>
+3  A: 

Single-quotes strings are not parsed for variables, so $_SESSION['$serverURL'] is probably not going to work as you expect. I suspect you mean $_SESSION[$serverURL] or $_SESSION['serverURL'].

Also calling filesize() and then readfile() will probably result in your script making two HTTP requests to fetch the file from the other server (unless this gets cached somehow). You could do it in one HTTP request using cURL, which may be a better option. Here is a brief example, you should be able to adapt it to do what you want. You might also want to consider forwarding other headers such as the Content-Type header from the other server (if reliable) rather than re-generating them yourself.

<?php

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'http://example.com');

//set callbacks to receive headers and content
curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'on_receive_header');
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'on_receive_content');

//send your other custom headers somewhere like here

if (false === curl_exec($ch)) {
    //handle error better than this.
    die(curl_error($ch));   
}

function on_receive_header($ch, $string) {
    //You could here forward the other headers received from your other server if you wanted

    //for now we only want Content-Length
    if (stripos($string, 'Content-Length') !== false) {
        header($string);   
    }

    //curl requires you to return the amount of data received
    $length = strlen($string);
    return $length;
}

function on_receive_content($ch, $string) {
    echo $string;

    //again return amount written
    $length = strlen($string);
    return $length;
}
Tom Haigh
Thank you for your reply. I actually made a mistake with the session variable. It should have been that $_SESSION['serverURL']. After correcting this, I still had the same problem. I even removed the line sending the filesize. Even then it still didn't work!
Abs
It actually worked when I removed the conditionals checking if the file exists. And I added the lines ob_clean();flush(); before the readfile(); I am going to attempt to implement your CURL size getter. :)
Abs