Force download with PHP and mime types

This is a basic feature post, but I wrote it to keep it handy.

If you need to link a document/picture/video or whatever file in your website, you can do it using an a-tag <a href…> link </a> , but what if you want to force this file to be download by the user instead of be opened by the browser?

To do that, you’ll need to write a bridge script between the page and the file, and then the link as it follows:

<a href='download.php?file=filename.jpg'>download image</a>

Then in the file download.php, the important code will be:

<?php
// .... before that, you should, well
// must check the file, the path,
// code injection...
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename=name_of_the_file_to_show.jpg"');
readfile("path/to/".$_GET["file"]);
?>

 

You can change the content-type to help browser identifying the file type. Below I attach a list of the most common/used:

 

Extension Type
(all files) application/octet-stream
avi video/x-msvideo
bmp image/bmp
css text/css
doc application/msword
dot application/msword
eps application/postscript
gif image/gif
gtar application/x-gtar
gz application/x-gzip
htm text/html
html text/html
ico image/x-icon
jpe image/jpeg
jpeg image/jpeg
jpg image/jpeg
js application/x-javascript
latex application/x-latex
mov video/quicktime
mp2 video/mpeg
mp3 audio/mpeg
mpa video/mpeg
mpe video/mpeg
mpeg video/mpeg
mpg video/mpeg
pdf application/pdf
pps application/vnd.ms-powerpoint
ppt application/vnd.ms-powerpoint
prf application/pics-rules
ps application/postscript
qt video/quicktime
rtf application/rtf
svg image/svg+xml
swf application/x-shockwave-flash
tgz application/x-compressed
tif image/tiff
tiff image/tiff
txt text/plain
xls application/vnd.ms-excel
zip application/zip

 

Another way would be using fileinfo or other functions available in php to query a file.

Read More

Reading files with PHP: “auto detect line endings”

With this PHP code:

<?php
$fp = @fopen("file.txt", "r");
if ($fp)
{
	$i = 1;
	while (($line = fgets($fp, 4096)) !== false)
	{
		echo "Line ".$i.": ".$line;
		$i++;
	}
}
?>

And this file.txt content:

aaaaa
bbbbb
ccccc
ddddd

You expect an output like:

Line 1: aaaaa
Line 2: bbbbb
Line 3: ccccc
Line 4: ddddd

A problem appears when you read a file created in a Mac environment (eg, \r instead of \n ). Then you could get something like:

Line 1: aaaaa bbbbb
Line 2: ccccc
Line 3: ddddd

To avoid that and always get a correct (and an expected!) performance, just add this line in the beginning of your code:

ini_set("auto_detect_line_endings", true);

Read More

Get web browser preferred language with PHP $_SERVER variables HTTP_ACCEPT_LANGUAGE

HTTP Language headers.
In php the way to get the language information sent by browser is with server reserved variables.

1
2
3
<?php
echo $_SERVER['HTTP_ACCEPT_LANGUAGE']);
?>

Which output (in my browsers):

  • ca,en-us;q=0.7,en;q=0.3 in Mozilla Firefox 13.0.1
  • es-ES in Internet Explorer 9 64b
  • ca-ES,ca;q=0.8 in Google Chrome 20.0

The “q-value” (when it’s shown in a value range between 0-1) indicates the percent that the browser prefer this language. If it’s not provided, we asume is 1 (maximum).

How to get the preferred language?
Here’s a simple function I’ve coded to get the preferred language according q-value in ISO 639-1 format:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function GetLanguageCodeISO6391()
{
	$hi_code = "";
	$hi_quof = 0;
	$langs = explode(",",$_SERVER['HTTP_ACCEPT_LANGUAGE']);
	foreach($langs as $lang)
	{
		list($codelang,$quoficient) = explode(";",$lang);
		if($quoficient == NULL) $quoficient = 1;
		if($quoficient > $hi_quof)
		{
			$hi_code = substr($codelang,0,2);
			$hi_quof = $quoficient;
		}
	}
	return $hi_code;
}
 
echo "Your browser preferred language is: ".GetLanguageCodeISO6391();
?>

To check what will output in your browser, go to this demo.

 

Read More

Warning: Cannot modify header information – headers already sent

Sometimes programming with PHP and trying to redirect pages with header(“location:”.$site); , it’s possible to get the following warning:
Warning: Cannot modify header information – headers already sent
This may occur for one of those reasons:

  • Start the file with (or including a file which starts with):
    1
    2
    3
    
    <?php
     
    // the code starts here, above there's a whitespace
  • Having previously in the script (or including a file which have it) whitespaces :
    1
    2
    3
    4
    5
    6
    7
    
    <?php
    // some example code...
    ?>
     
    <?php
    // more example code lines...
    ?>
  • Print or echo of a variable or text before sending headers.
  • HTML code before sending headers.
  • A mix of the above reasons…

Sometimes, depending on PHP version, everything seems to be okay but when you run the script, it doesn’t work. Let’s see an example:

Warning: Cannot modify header information – headers already sent by (output started at /full/route/to/file/which/previously/has/sent/headers/included.php:xx) in /full/route/which/has/sending/header/script.php on line xx

In script.php there is eg. header(“location:script2.php); and apparently headers has sent before in included.php.

In included.php:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// code
if ($lang== "es")
{
	include("spanish.inc.php");
}
elseif ($lang== "en")
{
	include("english.inc.php");
}
// more code
?>

Imagine your error is on include(“spanish.inc.php”) and obviously the included file is ok (eg. empty). Then why there’s an error? Some php versions or apache/iis configurations, make an include (or require, require_once, …) works as an output buffer. To avoid that, just wrap the “conflictive” code with:

ob_start();
// code which have include or require, etc.
ob_clean();

So, finally In included.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
// code
ob_start();
if ($lang== "es")
{
	include("spanish.inc.php");
}
elseif ($lang== "en")
{
	include("english.inc.php");
}
ob_clean();
// more code
?>

 

More information about those php functions here and here.

Read More