The 3-Step Guide to Downloading Files in Zend Expressive

A common requirement of web-based applications is to upload and download files. But, out of the box, there’s no simple way to download them in Zend Expressive. This tutorial shows you how - step-by-step.


How to Download Files with Zend Expressive

A common requirement of web-based applications is to upload and download files. But, out of the box, there’s no simple way to download them in Zend Expressive. This tutorial shows you how - step-by-step.

Recently, I was asked on Twitter by @dgoosens, about how to download files using Zend Expressive.

The timing was pretty good, as I’d done a simple implementation in a recent Zend Expressive project. So I knocked up a quick example and he, @acelayaa, and I talked it over, making various changes and suggestions along the way.

So, In today’s tutorial, I’m going to walk through a 3-step process for downloading files when using Zend Expressive.

1. The Download Functionality

Gladly, there’s not a lot involved in doing so. All you really need to do is make use of a Diactoros Stream, and some of PHP’s in-built functionality. Have a look at the function below, and then let’s step through what’s going on.

protected function downloadFile(ResponseInterface $response, $file)
{
    $body = new Stream($file);

    return $response
        ->withHeader('Content-Type', (new \finfo(FILEINFO_MIME))->file($file))
        ->withHeader('Content-Disposition', 'attachment; filename=' . basename($file))
        ->withHeader('Content-Transfer-Encoding', 'Binary')
        ->withHeader('Content-Description', 'File Transfer')
        ->withHeader('Pragma', 'public')
        ->withHeader('Expires', '0')
        ->withHeader('Cache-Control', 'must-revalidate')
        ->withBody($body)
        ->withHeader('Content-Length', "{$body->getSize()}");
}

The method can be placed in any Zend Expressive PageAction class, so it will be able to be passed a response object. $file can be taken from whatever source works for you; whether that’s a form or some other kind of (sanitized) user input. For the purposes of this example, I’m hard-coding it.

After that, I’ve created a variable, called $body, which contains the body of the response. This is a new Zend\Diactoros\Stream object, which reads in the contents of the file which $file points to.

With that done, I’m returning a new response object, after setting all of the headers necessary to download a file, and a few for elementary cache control.

The original gist I shared specified a generic ‘Content-Type’ value of application/octet-stream. But I wanted to put a bit more smarts in to this example. Given that, I made use of finfo, available in PHP since version 5.3.

This class is a quick and simple, not to mention memory saving, way of getting the mime-type data from a file, without reading it in to memory.

Together, the Content-Disposition, Content-Transfer-Encoding, and Content-Description headers tell the client that the file is going to be downloaded. If I’d removed the Content-Disposition header the file would be rendered in the client, if at all possible.

2. Using the Download Method

public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
{
   return $this->downloadFile($response, '/path/to/an/existing/file');
}

With the method complete, here’s how to make use of it. Create a project, based off of the Zend Expressive Skeleton Installer. Once it’s done you’ll find HomePageAction.php under /src/App/Action.

There, replace the original definition of __invoke, with the one above, and provide the path to a valid file on your filesystem.

3. Running the Application & Downloading the File

With that done, in your terminal, from the root directory of the project, call composer serve. This will launch the application where it will listen on port 8080, on your local machine.

Now that the application’s ready to accept requests, in your browser navigate to http://localhost:8080/. When the request finishes, you’ll see the file download or ask you for where to save the download, depending on the browser you’re using.

Conclusion

Zend Expressive does a great job of making application development a breeze. But out of the box, it doesn’t provide a specific way of how to download a file.

This tutorial has shown an elementary implementation that will do it. There are a lot of extras that could be added, to increase security, defensiveness, and functionality. But, as a teaching example, it does the job.


You might also be interested in these tutorials too...

Tue, Jun 26, 2018

What Are Delegator Factories and Why You Should Use Them

Ever wanted to dynamically expand the functionality of an object which you retrieve from your dependency injection container, based on different needs, yet without creating messy, hard to maintain configurations? Then you’re going to want to know about a powerful new technique - called Delegator Factories.

Mon, Aug 22, 2016

How To Use Laravel’s Eloquent ORM with Zend Expressive

Laravel’s Eloquent ORM isn’t likely the first one you think of when using Zend Expressive. You likely think of Zend\Db or Doctrine. But, with a little bit of work, it’s possible to use Eloquent with Expressive. Today’s tutorial shows you how - step-by-step.

Why Is My Website So Slow?!
Fri, Sep 27, 2019

Why Is My Website So Slow?!

Is your website (or web-based application) not performing like you expect it should? Not sure why or what to do about it? Then come learn about some key things you can do to have your website perform properly.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus