10

I'm working on a way to serve up MP3 files through PHP and after some help form the SO massive, I got it working here

However, that example doesn't appear to work when I use it as the source in an audio tag like this

<html>
    <head>
        <title>Audio Tag Experiment</title>
    </head>
    <body>

    <audio id='audio-element' src="music/mp3.php" autoplay controls>
    Your browser does not support the audio element.
    </audio>

    </body>
</html>

and here's the PHP

<?php

$track = "lilly.mp3";

if(file_exists($track))
{
header("Content-Transfer-Encoding: binary"); 
header("Content-Type: audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3");
header('Content-length: ' . filesize($track));
header('Content-Disposition: filename="lilly.mp3"');
header('X-Pad: avoid browser bug');
Header('Cache-Control: no-cache');

readfile($track);
}else{
    echo "no file";
}

So I'm thinking (and this may be a really bad idea, you tell me) that I might be able to set up Apache to serve a PHP file when someone requests an .MP3.

So I've three questions

  1. Will this work
  2. Good Idea / Bad Idea?
  3. What would I need to do? Would putting "AddType application/x-httpd-php .mp3" int he httpd conf do it?
Community
  • 1
  • 1
gargantuan
  • 8,888
  • 16
  • 67
  • 108

4 Answers4

17

There are some errors in your code:

  • A resource can only have one single Content-Type value. So you have to decide what media type you want to use. I suggest audio/mpeg.
  • You forgot to specify the disposition in Content-Disposition. If you just want give a filename and don’t want to change the disposition, use the default value inline.

The rest looks fine. But I would also send the 404 status code if the file cannot be found.

$track = "lilly.mp3";

if (file_exists($track)) {
    header("Content-Type: audio/mpeg");
    header('Content-Length: ' . filesize($track));
    header('Content-Disposition: inline; filename="lilly.mp3"');
    header('X-Pad: avoid browser bug');
    header('Cache-Control: no-cache');
    readfile($track);
    exit;
} else {
    header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found', true, 404);
    echo "no file";
}
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • Genius! Inline did the trick and I've tidied up the Content-Type and I'm now sending a 404. – gargantuan Oct 04 '09 at 16:39
  • This works well with most browsers, but (as of June 2020) iOS seems to require rather more complicated handing of request range to work properly. – SuprMan Jun 12 '20 at 21:54
4

You could simply make it so that you have a mod_rewrite rule to run every request for music/*.mp3 through your mp3.php file.

For example, something like this

RewriteEngine on
RewriteRule ^/music/(.*\.mp3)    /music/mp3.php?file=$1 [L]

mp3.php can then pick up the requested file from $_GET['file'], but if you go with this approach I recommend you sanity check the filename, to ensure it only references a file in the desired directory.

//ensure filename just uses alphanumerics and underscore
if (preg_match('/^[a-z0-9_]+\.mp3$/i', $_GET['file']))
{
    //check file exists and serve it

    $track=$_SERVER['DOCUMENT_ROOT'].'/music/'.$_GET['file'];
    if(file_exists($track))
    {
        header("Content-Type: audio/mpeg");
        header('Content-length: ' . filesize($track));
        //insert any other required headers...

        //send data
        readfile($track);
    }
    else
    {
        //filename OK, but just not here
        header("HTTP/1.0 404 Not Found");
    }

}
else
{
    //bad request
    header("HTTP/1.0 400 Forbidden");
}
Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
  • That works too, but the other answer was... purer I guess. Although that did remind me to use .htaccess to prevent people accessing the files directly. Cheers. – gargantuan Oct 04 '09 at 16:41
  • This solution lets you have .mp3 URLs, but allows you to intercept those requests with some PHP. It's not quite what you wanted, but I started it so I finished! – Paul Dixon Oct 04 '09 at 16:45
  • Good effort. Someone somewhere will find that useful. – gargantuan Oct 04 '09 at 17:17
  • Awesome. This is exactly what I was looking for. A way to force everything to be filtered by a php file. This *seems* like a good way to restrict download of files as well. – Jake Feb 28 '11 at 17:26
2

Use the header x-sendfile instead of readfile for better performanse.

http://john.guen.in/past/2007/4/17/send_files_faster_with_xsendfile/

wojtekk
  • 21
  • 1
0

This one works for me (in .htaccess):

<FilesMatch "mp3$">
    SetHandler application/x-httpd-php5
</FilesMatch>
djn
  • 3,950
  • 22
  • 21