<!DOCTYPE html>
<!-- ========================================== kroc camen of camen design ============================================= -->
<title>code · Under the Hood #4:  Getting a File’s Mime-Type From Apache</title>
<link rel="stylesheet" type="text/css" href="/design/design.css" />
<meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=no" />
<link rel="alternate" type="application/rss+xml" href="/code/rss" title="Just code" />
<link rel="canonical" href="/code/uth4_mime-type" />
<!-- =================================================================================================================== -->
	<h1><a href="/" rel="index">
		Camen Design
		<li><a href="/">all</a></li>
		<li><a href="/projects">projects</a></li>
		<li><a href="http://forum.camendesign.com">forum</a></li>
		<li><a href="/quote/">quote</a></li>
		<li><a href="/writing/">writing</a></li>
		<li><a href="/blog/">blog</a></li>
		<li><a href="/photo/">photo</a></li>
		<li><a href="/code/" rel="tag">code</a></li>
		<li><a href="/art/">art</a></li>
		<li><a href="/link/">link</a></li>
		<li><a href="/poem/">poem</a></li>
		<li><a href="/audio/">audio</a></li>
		<li><a href="/web-dev/">web-dev</a></li>
		<li><a href="/annoyances/">annoyances</a></li>
		<li><a href="/eve/">eve</a></li>
		<li><a href="/code-is-art/">code-is-art</a></li>
		<li><a href="/inspiration/">inspiration</a></li>
		<li><a href="/windows/">windows</a></li>
		<li><a href="/gift/">gift</a></li>
		<li><a href="/gaming/">gaming</a></li>
		<li><a href="/mac/">mac</a></li>
		<li><a href="/osnews/">osnews</a></li>
		<li><a href="/c64/">c64</a></li>
		<li><a href="/linux/">linux</a></li>
	<a rel="previous" href="/code/uth3_sqlite">
		older article →
	</a><a rel="next" href="/code/uth5_new-website-ish">
		← newer article
<!-- =================================================================================================================== -->
	<!-- date published or updated -->
	<time pubdate datetime="2008-07-12T12:57:00+01:00">
		<sup>12:57<abbr>pm</abbr> • 2008</sup>
		<abbr title="July">Jul</abbr> 12
	<!-- categories -->
		<li><a href="/code/uth4_mime-type" rel="bookmark tag">code</a></li>
		<li><a href="/code-is-art/uth4_mime-type">code-is-art</a></li>
		<li><a href="/web-dev/uth4_mime-type">web-dev</a></li>
	<!-- licence -->
		<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_GB">c</a>
		share + remix
	<!-- enclosure -->
	<a type="application/x-httpd-php" href="/code/uth4_mime-type/mime-types.php">
		mime-types <em></em>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<h1>Under the Hood #4: <br />Getting a File’s Mime-Type From Apache</h1>
	<strong>There is</strong> no sane way to get a file’s mime-type in PHP.<br />
	The <a href="http://uk3.php.net/mime_content_type"><code>mime_content_type</code></a> command is depreciated and
	not installed by default in PHP5.<br />
	The <a href="http://uk3.php.net/manual/en/book.fileinfo.php">FileInfo PECL extension</a> is not installed by
	default and can be insanely difficult to install.<br />
	<br />
	Thirdly, you can use a Unix call:

<pre><code>$mime_type = exec ("file -i -b '$file_path'");</code></pre>

	But that only works on Linux / BSD / Mac systems and not for Windows users, and it also requires that if you’re on
	a shared webhost that they allow you to use the <code>exec</code> command. This method also doesn’t always give
	accurate results.
	Lastly of course, you could write a simple function to look up a file-extension and return the appropriate

<pre><code>function mimeType ($extension) {
	switch ($extension) {
		case 'gif':	return 'image/gif';			break;
		case 'jpg':	return 'image/jpeg';			break;
		case 'png':	return 'image/png';			break;
		case 'pdf':	return 'application/pdf';		break;
		case 'zip':	return 'application/zip';		break;
		case 'exe':	return 'application/octet-stream';	break;

	But this is hardly automatic, requires constant maintenance when you comes across new types that enter your system
	and looks plain ugly in your code. It lacks elegance.

<hr />

	<strong>I found</strong> a cute solution to this problem through looking at the headers returned by Apache. For
	every file you access, Apache sets various HTTP Headers, here’s an example from a photo.

<pre>Date: Sat, 12 Jul 2008 11:24:33 GMT
Server:	Apache/2
Last-Modified: Tue, 17 Jun 2008 13:34:34 GMT
Etag: "2678b24-5edfe-cdfaae80"
Accept-Ranges: bytes
Content-Length: 388606
Content-Type: image/jpeg</pre>

	And lo, Apache is returning the mime-type automatically for any file-type. Since PHP has the ability to
	<a href="http://uk2.php.net/manual/en/features.remote-files.php">access remote URLs</a> with many of its
	functions, we could theoretically ask PHP to ping the local file we want to know the mime-type of and retrieve the
	relevant information from the HTTP headers.
	This is indeed possible, the
	<a href="http://uk2.php.net/manual/en/function.get-headers.php"><code>get_headers</code></a> function will give
	the HTTP Headers returned from a URL request as an array (if the second parameter is “1”). This function does
	not work without a full URL (“http://”), and therefore in order to get the mime-type of a local file, it needs
	to be in a publicly available location (even if only temporarily). You just prepend your domain name to the file
	path.<br />
	<br />
	Here’s the final example:

<pre><code>$domain = 'http://camendesign.com';
$file_path = 'data/content-media/photo/DSC00013.jpg';

$url_headers = get_headers ("$domain/$file_path", 1);
$mime_type = reset (explode (';', $url_headers['Content-Type']));

echo ($mime_type);</code></pre>

	Which correctly outputs:


	Apache sometimes returns a Content-Type value with the character set appended, e.g.
	“<code>text/html;charset=UTF-8</code>”, so “<code>explode (';', …)</code>” is used to break this apart,
	and <code>reset</code> returns the first array element.

	There are a lot of issues with this practice, which is why it is not used on this site:
			On some web-hosts, the firewall will prevent the loop-back call of the sever requesting a file on
			itself via an external route (the full domain URL). This will always work okay on your localhost
			and may work fine in many environments, but it’s not guaranteed, so always test first. <ins>I
			had to fall back to the stupid look-up function <samp>:/</samp></ins>
			This introduces lag! Remember that the server is loading an external URL, even if the URL ends up
			on the same machine. There is inherit lag due to the DNS lookup. Use an IP address if possible,
			and <strong>always</strong> cache the result. <strong>Do not run this code every time a user views
			a page!</strong> Use it once and once only for a file and then save the result somewhere!

<hr />

<h2>Update: Using the ‘Mime.types’ File</h2>
	Hiếu Hoàng writes:
		Debian’s default lighttpd.conf executes a “/usr/share/lighttpd/create-mime.assign.pl” file to get
		mime-type assignments. The “/etc/mime.types” which it uses is from the package mime-support.
		This would alleviate writing the look-up function.
	<cite>Hiếu Hoàng</cite>
	Sample output:

<pre>$ /usr/share/lighttpd/create-mime.assign.pl
mimetype.assign = (
	".ez" =&gt; "application/andrew-inset",
	".anx" =&gt; "application/annodex",
	".atom" =&gt; "application/atom+xml",
	".atomcat" =&gt; "application/atomcat+xml",
	".atomsrv" =&gt; "application/atomserv+xml",
	".avi" =&gt; "video/x-msvideo",
	".movie" =&gt; "video/x-sgi-movie",
	".mpv" =&gt; "video/x-matroska",
	".ice" =&gt; "x-conference/x-cooltalk",
	".sisx" =&gt; "x-epoc/x-sisx-app",
	".vrm" =&gt; "x-world/x-vrml",

	This file isn’t included in the Mac OS X PHP distribution, but I’ve copied it below with some small
	modifications (Output a PHP array, and Mac OS X’s ‘mime.types’ file is in ‘/etc/apache2/’ instead of

<pre>#!/usr/bin/perl -w
# (I don’t know Perl, and how to colour this right)
use strict;
open MIMETYPES, "/etc/apache2/mime.types" or exit;
print "\$mime_types = array (\n";
my %extensions;
while(&lt;MIMETYPES&gt;) {
	next if /^\w*$/;
	if(/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/) {
		foreach(split / /, $2) {
			# mime.types can have same extension for different
			# mime types
			next if $extensions{$_};
			$extensions{$_} = 1;
			print "\".$_\" =&gt; \"$1\",\n";
print ");\n";</pre>

	This could then be used to quickly put together a comprehensive (though massive) look-up function. I’ve saved a
	copy of the output of this script, enclosed at at the bottom of this article.
	The ‘mime.types’ file is interesting as there may be a way to write a real small function (probably regex) to
	pull out a mime-type on request, which would be a far more elegant (and practical) than my own solution. I’ll have
	to give that some thought.
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
	<nav><a href="http://forum.camendesign.com">‹ Discuss this in the Forum ›</a></nav>
	<a href="mailto:kroc@camendesign.com">kroc@camendesign.com</a>
		<a href="/code/uth4_mime-type.rem">Rem</a> •
		<a href="/code/uth4_mime-type.html">HTML</a> •
		<a href="/design/">CSS</a> •
		<a href="/.system/">PHP</a> •
		<a href="/.htaccess">.htaccess</a>
	<form method="get" action="https://duckduckgo.com">
		<input type="hidden" name="sites" value="camendesign.com" />
		<input type="search" name="q" placeholder="search…" />
		<input type="submit" value="Go" />
<!-- =================================================================================================== code is art === -->