13

这是我的代码:

$language = $_GET['soundtype'];
$word = $_GET['sound'];
$word = urlencode($word);
if ($language == 'english') {
    $url = "<the first url>";
} else if ($language == 'chinese') {
    $url = "<the second url>";
}
$opts = array(
  'http'=>array(
    'method'=>"GET",
    'header'=>"User-Agent: <my user agent>"
  )
);
$context = stream_context_create($opts);
$page = file_get_contents($url, false, $context);
header('Content-Type: audio/mpeg');
echo $page;

但我发现这运行得非常慢。

有没有可能的优化方法?

注意: $url是一个远程网址。

4

3 回答 3

9

It's slow because file_get_contents() reads the entire file into $page, PHP waits for the file to be received before outputting the content. So what you're doing is: downloading the entire file on the server side, then outputting it as a single huge string.

file_get_contents() does not support streaming or grabbing offsets of the remote file. An option is to create a raw socket with fsockopen(), do the HTTP request, and read the response in a loop, as you read each chunk, output it to the browser. This will be faster because the file will be streamed.

Example from the Manual:

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {

    header('Content-Type: audio/mpeg');

    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

The above is looping while there is still content available, on each iteration it reads 128 bytes and then outputs it to the browser. The same principle will work for what you're doing. You'll need to make sure that you don't output the response HTTP headers which will be the first few lines, because since you are doing a raw request, you will get the raw response with headers included. If you output the response headers you will end up with a corrupt file.

于 2012-12-03T09:09:26.100 回答
2

与其在输出之前下载整个文件,不如考虑像这样流式传输它:

$in = fopen($url, 'rb', false, $context);
$out = fopen('php://output', 'wb');

header('Content-Type: video/mpeg');
stream_copy_to_stream($in, $out);

如果你有胆量,你甚至可以尝试(但这绝对是实验性的):

header('Content-Type: video/mpeg');
copy($url, 'php://output');

另一种选择是使用内部重定向并使您的网络服务器代理您的请求。这将释放 PHP 做其他事情。另请参阅我关于 X-Sendfile 和朋友的帖子

于 2012-12-03T09:19:22.120 回答
2

正如@MrCode 所解释的,首先将文件下载到您的服务器,然后将其传递给客户端当然会导致下载时间加倍。如果要将文件直接传递给客户端,请使用readfile.

或者,考虑一下您是否不能简单地使用 a 将客户端重定向到文件 URL,header("Location: $url")以便客户端可以直接从源获取文件。

于 2012-12-03T09:17:41.937 回答