Hello,

I have a script that works great, basically i have a mysql database that contains information about files; like filename, description, size etc.

The information is displayed on a webpage in a nice formatted table. each file has a link next to it and once pressed the script below will get the file from server and hand it over to the visitor. It works great like it should.

My problem is i am sharing the script with loads of people, some want to force download images, audio files, video files, compression files and so on.

The script at the moment has 1 header and that is to force download a zip file. What i don't understand is how can i make my script below force download any file?

I did add about 15 headers for all popular video, audio, image files but it tends to use the last header in the script plus if for example i use just the 1 header

header('Content-Type: application/zip');

yet one of the files is a .exe file it appears in the download window when i click download as: an exe file but with a zip icon because it is using the zip header not the

header('Content-type: application/octet-stream');

header, basically once downloaded it shows as imagename.exe.zip

Here is my script, can someone please explain how i can get my script to force download any file? like i said the script is being shared so i need to ensure that any file will be forced to be downloaded.

It's basically going to be a relatively simple download manager script, i am just wondering how other such scripts manage to work around the issue i am not sure how to solve. If they were just .zip files then the script would work a treat but obviously i would want it to support more than one file type to force download.

Thanks
PHPLOVER

<?php

if( isset( $_GET['file'] ) ) {
    
    # get filename
    $getfile = $_GET['file'];

    # path to file
    $filepath = ''.$_SERVER['DOCUMENT_ROOT'].'/files/'.$getfile.'';

    # check file exisits, if it does force download/header
    if ( file_exists( $filepath ) ) {

    # Headers
    header('Content-type: application/zip');
    header('Content-disposition: attachment; filename='.$getfile.'');
    header("Content-length: " . filesize( $filepath ));
    readfile($filepath);

    } else { # else if file does not exist tell them and exit.
            echo "<h1> File Does Not exist </h1>";
            echo "The file you have requested does not exist. <br />";
            echo "Please click <a href=\"index.php\">here</a> to go back.";
            exit;
    }
} else { # else no parameter specified redirect back to downloads table
    header('LOCATION: index.php');
    exit;
}
?>

Recommended Answers

All 22 Replies

Member Avatar for Zagga

Hi phplover,

One approach may be to use the file extension to help.
You could use explode to set the file extension as a variable, then check it with code like . . .

if ($file_extension == ".exe"){
   header('Content-type: application/octet-stream');
}

Remember though that the filename (and extension) could be changed before you $_GET it.

Hope this helps.
Zagga

Hi,

I did think of this but thought there would be lots of if statements for each file type i want to force download.

any other easier way than using loads of if statements? , i know i could use a switch statement but still not very clean code.

I am trying to see if i can find a header type that will download several types of files with just one header type.

Thanks for the reply and help :)

Regards,
Mathew

Member Avatar for Zagga

I don't know of any "universal" header that could work with any file type.

Another option may be to force the files to be uploaded as .zip format (or copmpress them on the server) but this would mean the end user had to uncompress the file before use.


Zagga

Edit:

Firefox is recognising file type, my fault, it does for example:

test.zip
Which is a .jpg file

on popup download window but when it has downloaded it shows fine etc.

I am having a problem when filenames thou for some reason, i tested by using a filename 0My_MP31_4.mp3 and firefox downloaded it as a jpg file but when i changed it to MyMP3.mp3 it downloaded fine.

Any suggestions?

New Code:

<?php

if( isset( $_GET['file'] ) ) {

    # get filename
    $getfile = $_GET['file'];
    # get filetype
    $file_type = explode('.', $getfile);
    # path to file
    $filepath = ''.$_SERVER['DOCUMENT_ROOT'].'/files/'.$getfile.'';
    # check file exisits, if it does force download/header
    if ( file_exists( $filepath ) ) {

       # find the correct content type for header
       switch ($file_type) {
            case "exe":  $ctype="application/octet-stream"; break;
            case "zip":  $ctype="application/zip"; break;
            case "pdf":  $ctype="application/pdf"; break;
            case "gtr":  $ctype="application/x-gtar"; break;
            case "gz":   $ctype="application/x-gzip"; break;
            case "swf":  $ctype="application/x-shockwave-flash"; break;
            case "jpeg": $ctype="image/jpeg"; break;
            case "jpg":  $ctype="image/jpg"; break;
            case "bmp":  $ctype="image/bmp"; break;
            case "png":  $ctype="image/png"; break;
            case "psd":  $ctype="image/psd"; break;
            case "gif":  $ctype="image/gif"; break;
            case "tif":  $ctype="image/tiff"; break;
            case "ico":  $ctype="image/x-icon"; break;
            case "mp3":  $ctype="video/mpeg"; break;
            case "wmv":  $ctype="video/x-ms-wmv"; break;
            case "mpv":  $ctype="video/quicktime"; break;
            case "mp3":  $ctype="audio/mpeg"; break;
            case "wav":  $ctype="audio/x-wav"; break;
            default:     $ctype="application/force-download";
       }
        # force file download (headers)
        header("Content-type: $ctype");
        header('Content-disposition: attachment; filename='.$getfile.'');
        header("Content-Transfer-Encoding: binary");
        header("Content-length: " . filesize( $filepath ));
        readfile($filepath);
        exit;

    } else { # else if file does not exist tell them and exit.
            echo "<h1> File Does Not exist </h1>";
            echo "<p> The file you have requested does not exist.</p>";
            echo "Please click <a href=\"index.php\">here</a> to go back.";
            exit;
    }
} else { # else no parameter specified redirect back to downloads table
    header('LOCATION: index.php');
    exit;
}

?>

Thanks
PHPLOVER

EDIT:

Sorry for confusing.

It seems firefox downloads the file in the correct format but no matter what file i try to download it says in the download dialog window:


test.zip
Which is a .jpg file

Basically it says every file no matter that file type it is that it's a .jpg file althou the file downloads in correct format and opens/plays fine. I tried downloading from other websites and Firefox recognises the file type so i can only think there is something wrong with my script. Althou other browsers recognise the file type fine.

For easier explanation here is an image:

Notice it's a zip file but firefox says it's a jpg file althou it downloads it as a zip file and opens up fine.
[IMG]http://www2.picturepush.com/photo/a/3906165/640/3906165.jpg[/IMG]

This one is a .exe file yet firefox says it's a jpg file, althou again it downlaods as a .exe file and opens up fine.
[IMG]http://www3.picturepush.com/photo/a/3906166/640/3906166.jpg[/IMG]

After further investigating is seems that if i replace:

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

With

header("Content-type: application/zip");

and click to download zip file firefox recognises it, So after that it must be something to do with the $ctype/switch statement i think.

Thanks
PHPLOVER

Member Avatar for Zagga

Hi again Mathew,

I can't see why FireFox would think all the files on your site are jpegs, but gets it right on other sites.
What happens if you try to access the file directly instead of going through your script?


Zagga

Member Avatar for Zagga

Try changing the header to . . .

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

Hi,

I tried accessing the files directly not going through script and it worked saying test.zip which is a .zip file yet through the script trying also what zagga posted it still says test.zip which is a .jpg file. It does the same for any file type atlhough they are included in the switch statement, i thought it may have been firefox but it works not going through the script.

I tried this code Zagga and it's still the same.

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

Very confused. Unless it has something to do with exploding the file where there is a . , it explodes the file fine but maybe i have to add something else as explode places it into an array? not sure.

Thanks,
Mathew

Member Avatar for Zagga

Hi Mathew,

Try echoing $ctype at the end of the switch statement to see if it is what you are expecting.


Zagga

Hi Zagga,

How do i do that? as i tried but it just displays the download window and forces the download so echo $ctype; does not display on webpage.

Sorry for misunderstanding.

PHPLOVER

Member Avatar for Zagga

add the line

exit("CType: " . $ctype);

after the code has run through the switch statement. It will exit the script with the message.


Zagga

commented: Zagga was great in helping me solve a problem. Thanks so much :) +1

Hi Zagga,

Thanks so much for the help. I been working at it all day and thought a break would do me good so i went to my local for a pint to chill lol.

I came back and done what you said. It seems to go straight to the default in the switch statement. It echoed out:

CType: application/force-download

I tried this on .zip, .exe, .jpg, .png, .psd and it keeps going to the default in the switch statement and echoes out what i placed in bold tags above.

So i decided to comment out the default statement in the switch statememnt and it now works, i find it very strange thou.

I guess it is now solved, so thanks for everything.

Thanks,
PHPLOVER

Member Avatar for Zagga

I don't understand why it works, but as long as it does, it's good :)


Zagga

Hi Zagga,

It seems to have a slight problem.

I tested using a .txt file and althou i got the correct header in the switch statement, when i download the file and open it, it says:

<br />
<br />
<b>Notice</b>: Undefined variable: ctype in <b>C:\wamp\www\download-manager\process.php</b> on line <b>39</b><br />
Test File

Not sure why i get that error, i also find it strange why it is placing the error in the text file. Firefox also thinks a .exe file is a text file (again only firefox is thinking this), so confusing, get one thing solved and another occurs.

Thanks
PHPLOVER

Here is the code:

<?php

if( isset( $_GET['file'] ) ) {

    # get filename
    $getfile = $_GET['file'];
    # get filetype
    $file_type = explode('.', $getfile);
    # path to file
    $filepath = ''.$_SERVER['DOCUMENT_ROOT'].'/download-manager/files/'.$getfile.'';
    # check file exisits, if it does force download/header
    if ( file_exists( $filepath ) ) {

       # find the correct content type for header
       switch ($file_type) {
            case "exe":  $ctype="application/octet-stream"; break;
            case "zip":  $ctype="application/zip"; break;
            case "pdf":  $ctype="application/pdf"; break;
            case "gtr":  $ctype="application/x-gtar"; break;
            case "gz":   $ctype="application/x-gzip"; break;
            case "swf":  $ctype="application/x-shockwave-flash"; break;
            case "txt":  $ctype="text/plain"; break;
            case "jpeg": $ctype="image/jpeg"; break;
            case "jpg":  $ctype="image/jpg"; break;
            case "bmp":  $ctype="image/bmp"; break;
            case "png":  $ctype="image/png"; break;
            case "psd":  $ctype="image/psd"; break;
            case "gif":  $ctype="image/gif"; break;
            case "tif":  $ctype="image/tiff"; break;
            case "ico":  $ctype="image/x-icon"; break;
            case "mp3":  $ctype="video/mpeg"; break;
            case "wmv":  $ctype="video/x-ms-wmv"; break;
            case "mpv":  $ctype="video/quicktime"; break;
            case "mp3":  $ctype="audio/mpeg"; break;
            case "wav":  $ctype="audio/x-wav"; break;
            //default:     $ctype="application/force-download";
       }
        # force file download (headers)
        header("Content-type: " . $ctype);
        header('Content-disposition: attachment; filename='.$getfile.'');
        header("Content-Transfer-Encoding: binary");
        header("Content-length: " . filesize( $filepath ));
        readfile($filepath);
        exit;

    } else { # else if file does not exist tell them and exit.
            echo "<h1> File Does Not exist </h1>";
            echo "<p> The file you have requested does not exist.</p>";
            echo "Please click <a href=\"index.php\">here</a> to go back.";
            exit;
    }
} else { # else no parameter specified redirect back to downloads table
    header('LOCATION: index.php');
    exit;
}

?>

Hi,

In relation to the post above, i now also added some more mime types, like for doc, tgz, xls etc.

Firefox says they are text files, this seems very strange as IE8, Chrome recognises them.

I do get the undefined error in txt files still in IE8, Chrome etc same as i do with Firefox so that is something that needs fixing but also find it strange why i am having a problem that seems to be with Firefox recognising the file type. :confused:

I tried in latest version of opera to, if i keep the default in the switch statement Opera recognises the file type like chrome, ie8, but if i remove it Opera does not recognise the file types. So i added the default back to the switch statement but then Firefox says every file is a .jpg :confused:

This is my final code that has all the mimetypes i require:

<?php

if( isset( $_GET['file'] ) ) {

    # get filename
    $getfile = $_GET['file'];
    # get filetype
    $file_type = explode('.', $getfile);
    # path to file
    $filepath = ''.$_SERVER['DOCUMENT_ROOT'].'/download-manager/files/'.$getfile.'';
    # check file exisits, if it does force download/header
    if ( file_exists( $filepath ) ) {

       # find the correct content type for header
       switch ($file_type) {
            # Application
            case "exe":  $ctype="application/octet-stream"; break;
            case "zip":  $ctype="application/zip"; break;
            case "tgz":  $ctype="application/x-compressed"; break;
            case "z":    $ctype="application/x-compress"; break;
            case "gtr":  $ctype="application/x-gtar"; break;
            case "gz":   $ctype="application/x-gzip"; break;
            case "pdf":  $ctype="application/pdf"; break;
            case "doc":  $ctype="application/msword"; break;
            case "xla":  $ctype="application/vnd.ms-excel"; break;
            case "xlc":  $ctype="application/vnd.ms-excel"; break;
            case "xlm":  $ctype="application/vnd.ms-excel"; break;
            case "xls":  $ctype="application/vnd.ms-excel"; break;
            case "xlt":  $ctype="application/vnd.ms-excel"; break;
            case "xlw":  $ctype="application/vnd.ms-excel"; break;
            case "pps":  $ctype="application/vnd.ms-powerpoint"; break;
            case "ppt":  $ctype="application/vnd.ms-powerpoint"; break;
            case "pot":  $ctype="application/vnd.ms-powerpoint"; break;
            case "pub":  $ctype="application/x-mspublisher"; break;
            case "xls":  $ctype="application/vnd.ms-excel"; break;
            case "js":   $ctype="application/x-javascript"; break;
            case "swf":  $ctype="application/x-shockwave-flash"; break;
            # Text/Plain
            case "txt":  $ctype="text/plain"; break;
            case "rtx":  $ctype="text/richtext"; break;
            case "htm":  $ctype="text/html"; break;
            case "html": $ctype="text/html"; break;
            case "css":  $ctype="text/css"; break;
            # Image
            case "jpeg": $ctype="image/jpeg"; break;
            case "jpg":  $ctype="image/jpg"; break;
            case "jpe":  $ctype="image/jp"; break;
            case "bmp":  $ctype="image/bmp"; break;
            case "png":  $ctype="image/png"; break;
            case "psd":  $ctype="image/psd"; break;
            case "gif":  $ctype="image/gif"; break;
            case "tif":  $ctype="image/tiff"; break;
            case "tiff": $ctype="image/tiff"; break;
            case "ico":  $ctype="image/x-icon"; break;
            # Video
            case "mpeg": $ctype="video/mpeg"; break;
            case "mpg":  $ctype="video/mpeg"; break;
            case "wmv":  $ctype="video/x-ms-wmv"; break;
            case "mov":  $ctype="video/quicktime"; break;
            case "qt":   $ctype="video/quicktime"; break;
            case "avi":  $ctype="video/x-msvideo"; break;
            case "mp2":  $ctype="video/mpeg"; break;
            case "mpa":  $ctype="video/mpeg"; break;
            case "mpe":  $ctype="video/mpeg"; break;
            case "mpv2": $ctype="video/mpeg"; break;
            case "avi":  $ctype="video/mpeg"; break;
            case "avi":  $ctype="video/mpeg"; break;
            # Audio
            case "mp3":  $ctype="audio/mpeg"; break;
            case "wav":  $ctype="audio/x-wav"; break;
            # Default
            default:     $ctype="application/force-download";
       }
        # force file download (headers)
        header("Content-type: " . $ctype);
        header('Content-disposition: attachment; filename='.$getfile.'');
        header("Content-Transfer-Encoding: binary");
        header("Content-length: " . filesize( $filepath ));
        readfile($filepath);
        exit;

    } else { # else if file does not exist tell them and exit.
            echo "<h1> File Does Not exist </h1>";
            echo "<p> The file you have requested does not exist.</p>";
            echo "Please click <a href=\"index.php\">here</a> to go back.";
            exit;
    }
} else { # else no parameter specified redirect back to downloads table
    header('LOCATION: index.php');
    exit;
}

?>

Thanks,
PHPLOVER

I finally seem to have fixed it nearly :D

For the default in switch statememnt i replaced:

default:     $ctype="application/force-download";

With:

default:     $ctype="application/octet-stream";

After investigating it seems that the second mime type needs to be used for default, i guess the first one was incorrect.

Problem is i done what you said Zagga and added the code below at end of switch statement:

exit("CType: " . $ctype);

every file i click on seems to use the default

default:     $ctype="application/octet-stream";

. This suggests to me that it is not getting any of the content-type headers $ctype out of the switch statement and using default instead all the time.

Not sure why this would be happening. I no longer get the undefined variable error either. So one more thing to try and solve and that is why it is just jumping straight to the default in switch statement and not getting any of the set content-type in switch statement.

I was reading online and it is suppose to be very bad practice to use just application/octet-stream as this does not gaurantee to recognise all file types, hence why developers supposedly should specify the mime type to be on safe side. Although i am doing this but for some reason it's going straight to the default in switch statement.

Thanks
PHPLOVER

This article gives me an increased understanding.

Hi,

It's me again lol. It seems the explode function was not working correctly.

I done so looking online and replaced

# get filetype
$file_type = explode('.', $getfile);

With:

# get filetype
$file_type = substr(strrchr($getfile,'.'),1);

It seems to have now solved all my problems :)

Thanks
Mathew

Member Avatar for Zagga

I never even noticed that :)

So it was as simple as having the explode statement the wrong way round (slaps forehead).


Zagga

Hi Zagga,

What do you mean the explode was wrong way round? the delimiter comes first then the string so not sure why the explode did not work.

Thanks
PHPLOVER

Member Avatar for Zagga

Hi again Mathew,

My bad. I misread your post (too much coffee, too little sleep) and thought you still had the explode statement in there.

I can see why the explode wasn't working now though. You have exploded the string, so $file_type will be the filename, and $file_type will be the extension (as $file_type is now an array containing the exploded parts). We forgot to specify which part of $file_type to use.

So your switch statement should be
switch ($file_type) {

Zagga

Thanks Zagga,

I knew i was forgetting something, i said a few posts back that explode turns it into an array and forgot i had to specify it like you have shown above.

Thanks
PHPLOVER

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.