Hey, need help with your Magento, WooCommerce or Laravel project? I have some availalility. Contact me.

Working with temporary files in Magento 2

Controller, Magento 2

Recently i had to create an export in Magento 2 using PhpSpreadsheet. I created the sheet and added my data, but them i did hit a bump in the road: how do i download this file? PhpSpreadsheet offers two ways out of the box:

  • Writing to a file.

  • Echo the data.

The second method is anti Magento patterns where you return the response from your controller. You can fetch the output by using ob_flush(); functions, but that feels nasty too.

MageDispatch.com (ad)

Hey, I'm running a developer focussed newsletter called MageDispatch.com, which is for the community and by the community. Here you can share links that you think that the community should know about. If you like this kind of technical content you will like this newsletter too.

Creating temporary files

As option #2 is off the table, we investigate option 1. The first thing i tried is creating a temporary file as advised by PHP (Example from php.net):

$tmpfname = tempnam("/tmp", "FOO");

$handle = fopen($tmpfname, "w");
fwrite($handle, "writing to tempfile");
fclose($handle);

Now i have a pathe where i can let PhpSpreadsheet output its result and pass that to our controller. Sound easy, right? Well it turns out that Magento refuses to download this file as it is outside of the Magento directory.

Then i took another way, which makes more sense: asking Magento for the path to the tmp folder and write my file over there. This looks like this:

use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;

class Export {
    public function __construct(
      \Magento\Framework\Filesystem $filesystem
    ) {
      $this->filesystem = $filesystem;
    }

    public function save()
    {
        $directory = $this->filesystem->getDirectoryWrite(DirectoryList::TMP);

        $path = $directory->getAbsolutePath(sprintf('export-%s.xlsx', date('Ymd-His')));

        // Output your result to the file in $path
        $writer = new Xlsx;
        $writer->save($path);

        return $path;
    }
}

Now in your controller you need to use the path that is returned from the ->save() method. It look like this:

public function execute()
{
    $filename = $this->export->save();

    return $this->fileFactory->create('export-' . date('Ymd-His') . '.xls', [
        'type' => 'filename',
        'value' => $filename,
        'rm' => true,
    ]);
}

The 'rm' => true should make sure the the file is deleted after the download. You can read more about downloading files from controllers in this article.

Want to respond?