Morten Høgseth
Morten Høgseth

Reputation: 338

Downloading files using fopen(),fread(),feof()... Downloaded files are broken

I have a script that download's files from a server, all works nice. But when the file is downloaded either it's a jpg or png file. I cant seem to open it with any program to view it. (windows photo gallery or fireworks)

When i download a video file (avi). I cant open it with windows media player, but i can open it with vlc media player.

I always get a error msg, cant read file or file broken.

Here is the code, is it reliable or should i consider using fsocketopen or curl maybe.

I have tried altering the headers looking for more answers on the web, with no luck.

Anyone have an idea to whats wrong?

chmod("extensions_img/test.jpg",0755);

$fullPath = "http://www.website_url.com/extensions_img/test.jpg";

if ($fd = fopen ($fullPath, "r")) {

    $fsize = filesize("extensions_img/test.jpg");

    $path_parts = pathinfo($fullPath);
    $ext = strtolower($path_parts["extension"]);

    switch($ext) {

        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":
            $ctype = "image/jpg";
            break;
        case "jpg":
            $ctype = "image/jpg";
            break;
        case "mp3":
            $ctype = "audio/mp3";
            break;
        case "wav":
            $ctype = "audio/x-wav";
            break;
        case "wma":
            $ctype = "audio/x-wav";
            break;
        case "mpeg":
            $ctype = "video/mpeg";
            break;
        case "mpg":
            $ctype = "video/mpeg";
            break;
        case "mpe":
            $ctype = "video/mpeg";
            break;
        case "mov":
            $ctype = "video/quicktime";
            break;
        case "avi":
            $ctype = "video/x-msvideo";
            break;
        case "src":
            $ctype = "plain/text";
            break;
        default:
            $ctype = "application/force-download";
    }

    header("Pragma: public");

    header("Expires: 0");

    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

    header("Cache-Control: private",false);

    header("Content-type: " . $ctype);

    header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");

    header("Content-Transfer-Encoding: binary");

    header("Content-length: $fsize");
    header("Cache-control: public"); //use this to open files directly

    while(!feof($fd)) {
        $buffer = fread($fd, 4096);
    flush();
    }
}
fclose ($fd);

Upvotes: 1

Views: 11272

Answers (3)

mario
mario

Reputation: 145482

Using filesize($url) will download the file twice. And no idea why you are using fopen/fread anyway, when there is file_get_contents or fpassthru:

$data = file_get_contents($url);
$size = strlen($data);
$mime = current(preg_grep('/^Content-Type:/i', $http_response_header));

Please note how this also gets you the correct MIME type (just pass along the whole stirng via header for simplicity). Or even all the headers.

Upvotes: 0

samura
samura

Reputation: 4395

Try:

while(!feof($fd)) {
    echo fread($fd, 4096);
    flush();
}

You aren't echoing anything so probably your file is empty?

Upvotes: 1

Michael Berkowski
Michael Berkowski

Reputation: 270599

You are sending a Content-length: header containing a filesize you obtained incorrectly.

// You are opening a remote file:
$fullPath = "http://www.website_url.com/extensions_img/test.jpg";

// You are retrieving the filesize of a local file that may or may not exist
$fsize = filesize("extensions_img/test.jpg");

So, most likely, your Content-length really looks like the following:

 header("Content-length: 0");

...unless you have an actual local file of the same name, then you'll be supplying its filesize rather than the intended remote file.

According to filesize() documentation, some URL wrappers are supported. You might try to retrieve the filesize from the remote file:

As of PHP 5.0.0, this function can also be used with some URL wrappers. Refer to Supported Protocols and Wrappers to determine
which wrappers support stat() family of functionality.

$fsize = filesize($fullpath);

Upvotes: 0

Related Questions