2

I'm trying to send my mp3 files through a PHP script in order to hide the path to the file in the HTML. Everything works perfectly fine except half way into the mp3 chrome gives me a GET [URL] error. And the rest of the mp3 is just blank. The audio tag thinks it's still reading the file, but there is no sound.

This is how I'm sending the mp3 file from php:

if (file_exists($filename)) {
  header("Content-Transfer-Encoding: binary"); 
  header("Content-Type: audio/mpeg");
  header('Content-length: ' . filesize($filename));
  header('Content-Disposition: inline; filename="' . $filename . '"');
  header('X-Pad: avoid browser bug');
  header('Cache-Control: no-cache');


  readfile($filename);
  exit;
}
else {
  header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found', true, 404);
  echo "no file";
}

Edit: I added the Etag header. It seems to make things better. The problem still occurs but not as often. :-/

Edit2: I have noticed that the request headers sent to my PHP file is different from the headers sent directly to the mp3 file. Actually there's a HTTP_RANGE request sent to the mp3 file, but this range request is missing when I try to fetch things from PHP. (This is in Chrome). Any idea why this might be ?

Roozbeh15
  • 4,047
  • 6
  • 27
  • 30
  • 1
    Your script does not support ranges, the HTML 5 tag in chrome might request ranges. Make your script support ranges or try with smaller files. See as well: [Parsing HTTP_RANGE header in PHP](http://stackoverflow.com/questions/2209204/parsing-http-range-header-in-php). – hakre Mar 09 '12 at 01:28
  • My files aren't very big. The average size is less than 10MB. The largest might be 30MB. What is the default content-range? Can I set it to something like 50 MB? – Roozbeh15 Mar 09 '12 at 01:56
  • @hakre, this should be an answer. – davidethell Mar 09 '12 at 01:59
  • @hakre, After doing a little digging, I figured that the Range thing is not the issue since isset($_SERVER['HTTP_RANGE']) is not set when I get a request from the html5 audio tag. Any other ideas ? – Roozbeh15 Mar 09 '12 at 03:55
  • The path will still be available to someone who knows how to navigate a browser's inspector. Not saying its pointless, but you aren't really "hiding" anything like this. – Jon Egeland Mar 09 '12 at 20:35
  • I understand that I can never make it impossible for users to download my audio files, as if someone really wanted to they could simply write the output of their sound card to a file. I'm just trying to make it a little difficult so that random people won't be able to simply download stuff... – Roozbeh15 Mar 09 '12 at 20:48
  • @Roozbeh15 What you're doing doesn't help at all, though, as a user can just download the output of the script. –  Mar 09 '12 at 21:43
  • @duskwuff, I don't follow. What script? – Roozbeh15 Mar 09 '12 at 22:04
  • @Roozbeh15 duskwuff is saying that someone can view source of your HTML, then navigate to the url in the `src` attribute of the `audio` tag. Also, 10MB is a 'big' file when you're talking about web pages... most files the browser downloads are measured in KB. – kitti Mar 09 '12 at 22:45
  • @RyanP, If they navigate to the url, the php will simply open the file up and dump the output to them. It still won't give them the path to the file. Which is all I care about it at this point (I realize that there's no way to stop people form downloading the content)! I suppose 10 MB is large. – Roozbeh15 Mar 09 '12 at 22:57
  • I just asked the exact same question [here](http://stackoverflow.com/questions/16127864/html5-audio-reloads-the-page) – Yanick Rochon Apr 21 '13 at 04:56

1 Answers1

1

I know this is an old post but I was able to get this working with two files and index.php and example.php.

this is example.php that's checking a databse for a hash and a used field to know if the file has been used before

<?php

  if(isset($_GET['hash'])){
    $mysqli = new mysqli('host_here','user_here','password_here','database_here');
    $result = $mysqli->query("select * from hashes where hash = '{$_GET['hash']}'  limit 1");

    $row = $result->fetch_array();
  }

  if(isset($row['used']) && $row['used'] == 0){
    $mysqli->query("update hashes set used=1 where hash = '{$_GET['hash']}'");
    $filename = "example.mp3";
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: audio/mpeg");
    header('Content-length: ' . filesize($filename)); 
    //If this is a secret filename then don't include it.
    header('Content-Disposition: inline');
    //Otherwise you can add it like so, in order to give the download a filename
    //header('Content-Disposition: inline;filename='.$filename);
    header('Cache-Control: no-cache');


    readfile($filename);
    exit;
  }

  elseif(isset($row['used']) && $row['used'] == 1){
    die("you can't just download this dude");
  }

and here is index.php with the audio tag that will allow playing but not downloading of the mp3.

<html>
<body>
<?php

  $mysqli = new mysqli('localhost','root','','test_mp3');
  $rand = mt_rand();
  $hash = md5($rand);
  $result = $mysqli->query("insert into hashes (hash,used) values('$hash',0)");

?>
  <audio controls>
    <source src="example.php?hash=<?=$hash?>" type="audio/mpeg">
  </audio>
</body>
</html>

there is a database table with just a hash and a used field for this example to work

MiltoxBeyond
  • 2,683
  • 1
  • 13
  • 12
PHP_Developer
  • 386
  • 3
  • 11