问题描述
对于 Windows Chrome(可能还有许多其他浏览器),此代码适用于在 audio 元素中提供 mp3:
For Windows Chrome (and probably many other browsers), this code works for serving an mp3 in an audio element:
/** * * @param string $filename * @return IlluminateHttpResponse|IlluminateContractsRoutingResponseFactory */ public function getMp3($filename) { $fileContents = Storage::disk(AppHelpersCoachingCallsHelper::DISK)->get($filename); $fileSize = Storage::disk(AppHelpersCoachingCallsHelper::DISK)->size($filename); $shortlen = $fileSize - 1; $headers = [ 'Accept-Ranges' => 'bytes', 'Content-Range' => 'bytes 0-' . $shortlen . '/' . $fileSize, 'Content-Type' => "audio/mpeg" ]; Log::debug('$headers=' . json_encode($headers)); $response = response($fileContents, 200, $headers); return $response; }
但是当我用iPhone浏览同一页面时,mp3文件没有显示总时长,播放时显示直播".
But when I use an iPhone to browse to the same page, the mp3 file does not show the total duration, and when I play it, it says "Live broadcast".
我试图遵循这个问题的各种答案的建议 (HTML5<audio> Safari live broadcast vs not) 和我读过的其他文章,但似乎都没有效果.
I've tried to follow suggestions from various answers of this question (HTML5 <audio> Safari live broadcast vs not) and other articles I've read, but none seem to have an effect.
无论我如何更改标题,mp3 似乎在 Windows 上都能正常运行,而在 iOS 上不能运行.
No matter how I change the headers, the mp3 seems to function as desired on Windows and does not work on iOS.
如何调试我做错的地方?
How can I debug what I'm doing wrong?
这是 HTML:
<audio controls preload="auto"> <source src="{{$coachingCall->getMp3Url()}}" type="audio/mpeg"/> <p>Your browser doesnt support embedded HTML5 audio. Here is a <a href="{{$coachingCall->getMp3Url()}}">link to the audio</a> instead.</p> </audio>
推荐答案
哇,这是一个很难解决的问题.(我花了几天时间.)
Whoa, that was a very difficult problem to solve. (It took me days.)
而且我了解到不仅仅是 iOS 出现问题:Mac 上的 Safari 也无法正常工作.
And I learned that it wasn't just iOS that was having problems: Safari on Mac hadn't been working either.
现在我认为一切都适用于我测试过的每个浏览器.
Now I think everything works on every browser I've tested.
我真的很高兴我找到了这个例子来效仿.
I'm really glad I found this example to follow.
这是我的答案:
/** * * @param string $disk * @param string $filename * @return IlluminateHttpResponse|IlluminateContractsRoutingResponseFactory|SymfonyComponentHttpFoundationStreamedResponse */ public static function getMediaFile($disk, $filename) { $rangeHeader = request()->header('Range'); $fileContents = Storage::disk($disk)->get($filename); $fullFilePath = Storage::disk($disk)->path($filename); //https://stackoverflow.com/a/49532280/470749 $headers = ['Content-Type' => Storage::disk($disk)->mimeType($fullFilePath)]; if ($rangeHeader) { return self::getResponseStream($disk, $fullFilePath, $fileContents, $rangeHeader, $headers); } else { $httpStatusCode = 200; return response($fileContents, $httpStatusCode, $headers); } } /** * * @param string $disk * @param string $fullFilePath * @param string $fileContents * @param string $rangeRequestHeader * @param array $responseHeaders * @return SymfonyComponentHttpFoundationStreamedResponse */ public static function getResponseStream($disk, $fullFilePath, $fileContents, $rangeRequestHeader, $responseHeaders) { $stream = Storage::disk($disk)->readStream($fullFilePath); $fileSize = strlen($fileContents); $fileSizeMinusOneByte = $fileSize - 1; //because it is 0-indexed. http://www.51sjk.com/Upload/Articles/1/0/338/338601_20221208100101251.16 list($param, $rangeHeader) = explode('=', $rangeRequestHeader); if (strtolower(trim($param)) !== 'bytes') { abort(400, "Invalid byte range request"); //Note, this is not how https://stackoverflow.com/a/29997555/470749 did it } list($from, $to) = explode('-', $rangeHeader); if ($from === '') { $end = $fileSizeMinusOneByte; $start = $end - intval($from); } elseif ($to === '') { $start = intval($from); $end = $fileSizeMinusOneByte; } else { $start = intval($from); $end = intval($to); } $length = $end - $start + 1; $httpStatusCode = 206; $responseHeaders['Content-Range'] = sprintf('bytes %d-%d/%d', $start, $end, $fileSize); $responseStream = response()->stream(function() use ($stream, $start, $length) { fseek($stream, $start, SEEK_SET); echo fread($stream, $length); fclose($stream); }, $httpStatusCode, $responseHeaders); return $responseStream; }