skip to Main Content

This is driving me crazy. I’m using a PHP function to download an Excel file. This works fine in my local environment and testing server, but on the production server, instead of downloading the spreadsheet, it prints a lot of garbled text to the browser window. It looks like the "PK" code for a zip file. The result is inconsistent. I reduce the number of columns and it works. Then I added one more column and it breaks. Then the next day it works. Then I add another column and it breaks. The same function works in other areas of the app, it’s just when I try to export this one file.

This is a Symfony 4.4 app running on PHP 8.1. All environments should be identical.

        $filePath = $path . $fileName;

        $response = new Response();
        $response->headers->set('Content-Type', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        $response->headers->set('Content-Disposition', "attachment;filename={$fileName}");
        $response->headers->set('Cache-Control', "no-cache");
        $response->headers->set('Expires', "0");
        $response->headers->set('Content-Transfer-Encoding', "binary");
        $response->headers->set('Content-Length', filesize($filePath));
        $request = Request::createFromGlobals();
        $response->prepare($request);
        $response->setContent(readfile($filePath));
        $response->sendContent();
        unlink($filePath);

        return $response;

2

Answers


  1. Chosen as BEST ANSWER

    I got it. Instead of trying to set all those headers manually I just used the ResponseHeaderBag. Works now.

    return $this->file("export/".$fileName, $fileName, ResponseHeaderBag::DISPOSITION_INLINE);
    

  2. Right off the bat, readfile() does not return the contents of the file, but rather outputs it directly so:

    $response->setContent(readfile($filePath));
    

    is definitely wrong.

    Instead use file_get_contents:

    $response->setContent(file_get_contents($filePath));
    

    I would refactor the code to read the file contents and set the Content-Length according to how many bytes $fileContents is:

    $fileContents = file_get_contents($filePath);
    
    $response->headers->set('Content-Length', strlen($fileContents));
    
    $request = Request::createFromGlobals();
    $response->prepare($request);
    $response->setContent($fileContents);
    

    (strlen() always returns the number of bytes in a string, not characters).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search