I have some problems with serving mp3's with Laravel 4.2
I have some files which should be played by flashplayer.
public function get($filename)
{
$file = new Symfony\Component\HttpFoundation\File\File(storage_path().DbConfig::get('system.upload_dir').'/'.DbConfig::get('system.upload_music').'/'.$filename);
$response = Response::make(file_get_contents(storage_path().DbConfig::get('system.upload_dir').'/'.DbConfig::get('system.upload_music').'/'.$filename));
$response->header('Content-Type', $file->getMimeType());
$response->header('Content-Length', $file->getSize());
$response->header('Content-Transfer-Encoding', '');
$response->header('Accept-Range', 'bytes');
$response->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
$response->header('Connection', 'Keep-Alive');
return $response;
}
This serves the file - if opened in chrome it lounches chrome's default player and the music plays, the same thing is when I louch it with a flashplayer.
But I'm not able to wind the record.
If I serve the file with apache (instead of Laravel controller) it works fine.
I would be grateful if anyone could help me with this issue.
UPDATE
Headers when served via Laravel:
HTTP/1.1 200 OK
Date: Thu, 01 Oct 2015 18:43:59 GMT
Server: Apache/2
Cache-Control: must-revalidate, post-check=0, pre-check=0, private
Content-Transfer-Encoding:
Accept-Range: bytes
Connection: Keep-Alive, Keep-Alive
Set-Cookie: laravel_session=eyJ[...]D; expires=Thu, 01-Oct-2015 20:44:00 GMT; Max-Age=7200; path=/; httponly
Vary: Accept-Encoding,User-Agent
Keep-Alive: timeout=2, max=100
Transfer-Encoding: chunked
Content-Type: audio/mpeg
Headers when served without Laravel:
HTTP/1.1 200 OK
Date: Thu, 01 Oct 2015 18:51:16 GMT
Server: Apache/2
Last-Modified: Fri, 13 Mar 2015 04:03:23 GMT
ETag: "ead61-5112394e338c0"
Accept-Ranges: bytes
Content-Length: 961889
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: audio/mpeg
The major difference in the headers that stood out for me is the absence of Content-Length
when using Laravel. I don't know how the flash player works, but I would venture it needs to know the length of the file to be able to seek (i.e. wind) to any position.
However, the code you posted explicitly sets that header. Searching a bit, I found this Laravel issue: 2079. They suggest using the undocumented Response::stream
to send the file contents like so:
Response::stream(function() use($fileContent) {
echo $fileContent;
}, 200, $headers);
Here's the Symfony doc for StreamedResponse
.
Update on Response type
You are serving static files, so the appropriate response type to use is the BinaryFileResponse
through Laravel's Response::download($pathToFile, $name, $headers)
.
(by the way, you can use that third argument to set headers)
Update on Content-Length
The source of Symfony's Response
class holds the key to Content-Length
getting removed:
/**
* Prepares the Response before it is sent to the client.
*
* This method tweaks the Response to ensure that it is
* compliant with RFC 2616. Most of the changes are based on
* the Request that is "associated" with this Response.
*
* @param Request $request A Request instance
*
* @return Response The current response.
*/
public function prepare(Request $request)
{
$headers = $this->headers;
if ($this->isInformational() || $this->isEmpty()) {
// [snip]
} else {
// [snip]
// Fix Content-Length
if ($headers->has('Transfer-Encoding')) {
$headers->remove('Content-Length');
}
// [snip]
}
// [snip]
}
RFC 2616 does not allow both Transfer-Encoding
and Content-Length
headers to be used and Symfony enforces this. Quote:
4.4 Message Length
[...]
3.If a Content-Length header field (section 14.13) is present, its
decimal value in OCTETs represents both the entity-length and the
transfer-length. The Content-Length header field MUST NOT be sent
if these two lengths are different (i.e., if a Transfer-Encoding
header field is present). If a message is received with both a
Transfer-Encoding header field and a Content-Length header field,
the latter MUST be ignored.
Also, according to this SO question, Content-Transfer-Encoding
is only used in emails. Transfer-Encoding
is used otherwise.
Another thing: you used Accept-Range
in Laravel instead of Accept-Ranges
in Apache.
Lastly, try a plain download
response without setting any headers and see what you get. Then add more headers as needed.