0

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
violator667
  • 469
  • 4
  • 19
  • What does "wind" mean in this context? I suggest you log the responses headers you receive in both Apache and Laravel and post them here so we can see what's different. – bernie Sep 28 '15 at 05:11
  • @bernie look at my update, "wind" means that I can skip some part of the record using player bar, and later I can rewind it to the begining. – violator667 Oct 01 '15 at 18:55

1 Answers1

1

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.

Community
  • 1
  • 1
bernie
  • 9,820
  • 5
  • 62
  • 92
  • even if I use `Response::stream` I'm not able to set `Content-Length` header - I can set `Some-Dummy-Header` and it's present in the response headers but Laravel just won't let me set content-lenght :/ – violator667 Oct 02 '15 at 12:38
  • I was fighting with this issue all day yesterday and nothing - I tried all types of responses `make` , `download`, `stream` and nothing - I can't set `Content-Length` in any of the responses, so I had to make some workaround to achieve what I want (access to mp3, and Laravel model in the (almost) same time. I had to move files from `storage` to `public` dir and redirect to it (serving with apache) after accessing it's model. Anyhow thank you for your answer - it helped me to understand the issue. – violator667 Oct 03 '15 at 06:19