Martin Fric
Martin Fric

Reputation: 730

php force download using header() broken downloaded file

So firstly i would like to tell that i am quite newbie in header usage and playing with output buffers. So im developing php portlet for liferay and i have some problem with file downloads. I tried to do it simply with 'a href' but problem is that file uploaded via php is unavailable till apache is refreshed so i tried another way with header() function.

So i will try to explain my problem. When i tried following code on simple php project it works fine :

    <?php

$path = "/mysecretdir/upload/"; // change the path to fit your websites document structure
$fullPath = $path.$_GET['download_file'];

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

    $fsize = filesize($fullPath);

    $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)) {
        echo fread($fd, 4096); 
    flush();
    }
}
fclose ($fd);
?>

from index.php im calling it with href="download.php?download_file=/something/"

Ok but now the point. When i am using it in liferay portlet the file is weird. It puts whole generated HTML file in the created file. So its broken. I dunno why. I dont know if headers are sending some information from elsewhere and also dont know how to fix it.

I was searching for hours for some solution but dont know how to make somehow "session" for headers, because i think there is problem with them. Becasue without echo it prints only content of file and in other project - more simple php app not as a part of portal it works! but maybe i am wrong.

So please can someone help me? Any advise?

Upvotes: 1

Views: 1620

Answers (2)

Olaf Kock
Olaf Kock

Reputation: 48057

I can't help you on specific php-portlet problems (I never used Liferay with php) but it sounds like you get the whole page HTML generated "around" your downloaded file. This is what you get when you just render a portlet: A portlet is always embedded in a HTML page, thus you cannot provide specific headers on the HTTP level with the standard render output of a portlet.

What you want is the serveResource lifecycle phase of a portlet. This will allow you to serve content that is not part of the page, but you have full control over the download and HTTP headers. How to do that with php portlets, I'll have to leave to you.

Edit (additional information): As you asked in the comments, I found an older (might need to be adapted) Wiki article that talks about using state=exclusive to do the same trick - instead of the serveResource that I suggested above. I don't know if this is due to the age of the article or because PHP portlets don't support that lifecycle, but you might find something there and in the related&linked articles. Note: serveResource would - if I'm not mistaken - generate a p_p_lifecycle=2 parameter, while this example uses p_p_lifecycle=0 (render) and p_p_state=exclusive. Try if this fits your requirements

However, please consider Marc B's comment about your code being insecure and too hardcoded. There are better solutions for the underlying problem - e.g. Liferay provides the Document Library to up/download files out of the box. And that doesn't have these problems.

Upvotes: 1

Zathrus Writer
Zathrus Writer

Reputation: 4331

If you started your PHP file with a space or any other character, headers won't work and the display/download will fail. Make sure you don't have anything before the <?php tag.

Other than that, this should work (if you're not getting errors in the browser).

On the other hand, if you see the actual PHP code, your server does not support PHP.

Upvotes: 0

Related Questions