最近,在项目中尝试使用html5中的audio标签做web版的播放器,因为涉及到付费的音频,所以需要对用户权限做一些判断,然后动态输出mp3。但是,动态生成的mp3在chrome中无法正常拖动。

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"><title>Test</title>
</head>
<body>
<audio src="http://localhost/demo/mp3.php"  controls="controls"></audio>
</body>
</html>
header("Content-type:audio/mpeg");
header('Content-Length: ' . filesize("1.mp3"));
echo file_get_contents("1.mp3");

将audio中的src修改成mp3源文件路径(在chrome中拖动正常):

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<audio src="http://localhost/demo/1.mp3"  controls="controls"></audio>
</body>
</html>

猜想,莫非audio播放mp3后缀的音频文件才能拖动进度条?

尝试配置apache解析后缀为mp3的php文件,将mp3.php改成mp3.mp3,问题依然存在。取消掉apache解析后缀为mp3文件的行为,将audio标签中的src修改为mp3源文件路径,利用chrome控制台查看audio标签如何请求以及响应。

1.mp3的请求信息:

请求头里面主要包含Range字段。 响应信息如下:Status Code:206 Partial Content。

响应头信息:

主要包含Accept-Ranges、Content-Range字段。 很明显,chrome请求mp3文件时采用的是断点续传。 当点击进度条时,相应的请求信息及响应信息如下:

当点击进度条时,chrome浏览器会发起一条http请求,依然采用断点续传。根据以上分析,若要使动态生成的mp3能拖动进度,就必须在php文件中实现断点续传,在php中构造相应的响应头即可。

最终,mp3.php代码如下:

<?php
$file = "1.mp3";
$fileSize = filesize($file);
$etag = md5(filemtime($file));
$fp = fopen($file, 'rb');
if (!$fp) {
    die('Could not open file');
}
$start = 0;
$end = $fileSize - 1;

if (isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])) {
    //获取请求头中的Range字段
    $range = explode('-', substr($_SERVER['HTTP_RANGE'], strlen('bytes=')));

    $start = $range[0];
    if ($range[1] > 0) {
    	$end = $range[1];
    }

    //构造断点续传响应头
    header('HTTP/1.1 206 Partial Content');
    header('Status: 206');
    header('Accept-Ranges: bytes');
    header('Content-Range: bytes ' . $start. '-' . $end . '/' . $fileSize);
    header('Content-Length: ' . ($end - $start + 1));
} else {
    header('Content-Length: ' . $fileSize);
}

header('Content-Type: audio/mpeg');
header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T', filemtime($file)));
header('ETag: "' . $etag . '"');
header('Expires: 0');
if ($start > 0) {
    fseek($fp, $start); //移动文件指针
}
$bytesPosition = $start;
while (!feof($fp) && $bytesPosition <= $end) {
    $chunk = 1024 * 1024 * 50; //每次读取50k
    if ($bytesPosition + $chunk > $end + 1) {
    	$chunk = $end - $bytesPosition + 1;
    }
    $chunk = fread($fp, $chunk);
    if (!$chunk) {
    	die('Could not read file');
    }
    print($chunk);
    flush();
    $bytesPosition += $chunk;
}
fclose($fp);