Download a file in a controller in Magento 2

Controller, Magento 2, Quick Tip

Today i had to create a file download from a Magento 2 controller. I turnes out that this is quite simple. Now, it's good to know that there are 2 options here:

  • You have the file contents available. This is the case when you created an CSV with data for example.

  • The file you want to offer to download is already somewhere on disk. This can be something the user uploaded before.

MageTested.com (ad)

Do you want your Magento store to be more reliable? Tired of things that suddenly break the checkout process without anyone noticing? You can hire my services to kickstart End-2-End testing for your Magento store. This way you know for sure that your store is behaving as expected before releasing that new feature or that update.

View MageTested.com for more information.

Dynamic file contents

First, we need the \Magento\Framework\App\Response\Http\FileFactoryclass:

use Magento\Framework\App\Response\Http\FileFactory;

public function __construct(
    Action\Context $context,
    FileFactory $fileFactory
) {
    parent::__construct($context);
    $this->fileFactory = $fileFactory;
}

Then in our execute method we return the result of the create method:

public function execute()
{
    $contents = $this->orderExport->generateCsv();

    return $this->fileFactory->create('order-export.csv', $contents);
}

And that's it, when you visit this endpoint you will get a file download called order-export.csv.

Existing file

Now this requires an extra dependency as we need to get the path to our file:

use Magento\Framework\Filesystem\DirectoryList;
use Magento\Framework\App\Response\Http\FileFactory;

public function __construct(
    Action\Context $context,
    FileFactory $fileFactory,
    DirectoryList $directory
) {
    parent::__construct($context);
    $this->fileFactory = $fileFactory;
    $this->directory = $directory;
}

Next we calculate the full path and instead of a string with contents, pass an array as contents:

public function execute()
{
    $path = $this->directory->getPath(\Magento\Framework\App\Filesystem\DirectoryList::PUB) . '/uploads/' . $this->getFilename();

    return $this->fileFactory->create($model->getValue(), [
        'type' => 'filename',
        'value' => $path,
    ]);
}

In this case the file must be present in the pub/uploads folder. You can check this file for all folders available by default in Magento.

Remove the file after download

Do you need to remove the file when the download is done? Magento has got your back, just add the rm key:

return $this->fileFactory->create($model->getValue(), [
    'type' => 'filename',
    'value' => $path,
    'rm' => true,
]);

Now your file is deleted after the download is complete.

Want to respond?