Home > Software engineering >  Excel exported as garbled PK zip file to browser
Excel exported as garbled PK zip file to browser

Time:10-10

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;

CodePudding user response:

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).

CodePudding user response:

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);
  • Related