3

I have created a function for my Symfony application based on this answer: https://stackoverflow.com/a/13025363/1749653

The function is this:

public function getForceDownloadResponse($file_path, $file_name){
    $file_info = finfo_open(FILEINFO_MIME_TYPE);
    $mine_type = finfo_file($file_info, $file_path.$file_name);
    finfo_close($file_info);

    $response = new Response();
    $response->headers->set('Cache-Control', 'private');
    $response->headers->set('Content-type', $mine_type);
    $response->headers->set('Content-Disposition', 'attachment; filename="' . $file_name . '"');
    $response->headers->set('Content-length', filesize($file_path.$file_name));

    $response->sendHeaders();
    $response->setContent(readfile($file_path.$file_name));

    return $response;
}

And that all worked out quite well... until someone tried downloading a text file with it. For some reason, all text files are downloaded with the wrong filename. They all follows this pattern: Actual_File_Name-,attachment so, for example: new_text.txt-,attachment etc

After some investigation I only found one irregularity with text files as a whole. Somehow the response header fields are dublicated. Where binary files will output something like this:

Cache-Control:private
Connection:close
Content-Disposition:attachment; filename="Jellyfish.jpg"
Content-Length:775702
Content-Type:image/jpeg
Date:Mon, 10 Nov 2014 09:14:41 GMT
Server:Apache/2.4.7 (Win32) OpenSSL/1.0.1e PHP/5.5.6

Any text file will look like this:

Cache-Control:private
Cache-Control:private
Connection:Keep-Alive
Content-Disposition:attachment; filename="full.txt"
Content-Disposition:attachment; filename="full.txt"
Content-Length:15
Content-Type:text/plain; charset=UTF-8
Date:Mon, 10 Nov 2014 09:06:00 GMT
Keep-Alive:timeout=5, max=97
Server:Apache/2.4.7 (Win32) OpenSSL/1.0.1e PHP/5.5.6

I suspect the uninteded behavior is rooted somewhere in whatever causes that Content Header behavior. But I can't for the life of me figure out how those come about.

So, if anyone knows more about how these get created, what might cause them to behave as this or even how to solve the problem outright, any help would be greatly appreciated.

Cœur
  • 37,241
  • 25
  • 195
  • 267
C.Funken
  • 51
  • 5

1 Answers1

0

Finally figured it out. Posting it here in case someone, somewhere has a similar problem and finds it useful:

So. It turns out that the problematic line was this:

$response-sendHeaders()

For binary files, this line is critical in making it work. Text files don't need it though and will in fact break like seen above. I have not found out exactly 'why' it behaves like this. But the solution was to simply check for whether or not the requested file is a text file or not. (with a build in exception for .html files, but thats more of a optional feature / quirk of the project and can thus be safely ignored)

This is what the function looks like now and its working fine:

public function getForceDownloadResponse($file_path, $file_name){
    $file_info = finfo_open(FILEINFO_MIME_TYPE);
    $mime_type = finfo_file($file_info, $file_path.$file_name);
    $text = (substr(finfo_file($file_info, $file_path.$file_name), 0, 4) == 'text') ? 1 : 0;
    finfo_close($file_info);

    $response = new Response();
    $response->headers->set('Cache-Control', 'private');
    $response->headers->set('Content-type', $mime_type);
    $response->headers->set('Content-Disposition', 'attachment; filename="' . $file_name . '"');
    $response->headers->set('Content-length', filesize($file_path.$file_name));

    if(!$text || $mime_type == 'text/html'){
        $response->sendHeaders();
    }

    $response->setContent(readfile($file_path.$file_name));

    return $response;
}

Since I still don't know what actually caused the different behaviors its not as safe and solid as I'd like it to be, but it does the job just fine so far.

C.Funken
  • 51
  • 5