★ Laravel-medialibrary v7 preview: multi file downloads
Tags: php, laravel
Published: 2020-01-02 22:53:12

---
title: ★ Laravel-medialibrary v7 preview: multi file downloads
published: true
date: 2017-12-12 22:38:53 UTC
tags: php, laravel
canonical_url: https://freek.dev/930-laravel-medialibrary-v7-preview-multi-file-downloads
---

[laravel-medialibrary](https://docs.spatie.be/laravel-medialibrary/) is a powerhouse package that can help handle media in a Laravel application. It can [organise your files across multiple filesystems](https://docs.spatie.be/laravel-medialibrary/v6/advanced-usage/working-with-multiple-filesystems), [generate thumbnails](https://docs.spatie.be/laravel-medialibrary/v6/converting-images/defining-conversions), [optimize images](https://docs.spatie.be/laravel-medialibrary/v6/converting-images/optimizing-converted-images) and much much more.

My team and I are hard at work creating a new major version, v7, that adds a lot of awesome features such as [responsive images](https://twitter.com/freekmurze/status/939161592725409796), vue components to assist with uploading, single file collections, ... In this post I'd like to highlight one cool handy feature that's coming to v7: multi file downloads.

In our package every file is associated with a `Media` model. To download a single file you can do this in a controller:

```
public function download(Media $media) 
{
   return response()->download($media->getPath());
}
```

Because `Media` [implements](https://github.com/spatie/laravel-medialibrary/blob/ac8b8df1d2266260a20dc181b528050e09b1c9b4/src/Media.php#L236-L242) [Laravel's Responsable interface](https://laravel-news.com/laravel-5-5-responsable) you can also write that a bit shorter:

```
public function download(Media $media) 
{
   return $media;
}
```

Pretty sweet! But what if you want to download multiple files at once? Currently the medialibrary won't help you with this and you need to take care of this yourself. You could generate a zip file with all media you want to download on your server and then do

```
return response()->download($pathToZippedMediaFiles);
```

But maybe you want to download big files, in that case creating the zip takes a while. Or maybe the files are stored on a remote service like S3, so now it takes even longer because the files need to be copied over first.

I'm happy to share that medialibrary v7 will solve this for you. It includes a `ZipStreamResponse` class that allows you to respond with a stream. Files will be zipped on the fly and you can even include files from multiple filesystems.

Let's take a look at an example on how to use `ZipStreamResponse`. We're going to create two routes. Visiting `add-files` will add some files to our medialibrary, visiting `download-files` will download them. I'm using routes here for demonstration purposes, in a real world app you'd probably use `ZipStreamResponse` in a controller.

```
Route::get('add-files', function() {
    //create a regular model
    $article = Article::create();

    // add a file to the downloads collection on the local disk
    $article
        ->addMedia($pathToAFile)
        ->toMediaCollection('downloads');

    // add a file to the downloads collection on the s3 disk
    $article
    ->addMedia($pathToABigFile)
    ->toMediaCollection('downloads', 's3');

    return 'files added!';
});

Route::get('download-files', function() {
    //get all files in the download collection
    $allMedia = Article::first()->getMedia('downloads');

    // download them in a streamed way, so no prob if your files are very large
    return ZipStreamResponse::create('my-files.zip')->addMedia($allMedia);
});
```

That last line is the most important one. It's kinda cool that the zip is created on the fly and that it pulls data from both the local disk and s3.

Coding `ZipStreamResponse` up was easier than I thought it would be. The [maennchen/zipstream-php](https://github.com/maennchen/ZipStream-PHP) does the hard work of creating a zip stream. All I need to do was to integrate the provided `ZipStream` class in our medialibrary. Here's the entire source code of `Spatie\MediaLibrary\ZipStreamResponse`:

```
namespace Spatie\MediaLibrary;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Contracts\Support\Responsable;
use Spatie\MediaLibrary\Media;
use Symfony\Component\HttpFoundation\StreamedResponse;
use ZipStream\ZipStream;

class ZipStreamResponse implements Responsable
{
    /** string */
    protected $zipName;

    /** Illuminate\Support\Collection */
    protected $mediaItems;

    public static function create(string $zipName)
    {
        return new static($zipName);
    }

    public function __construct(string $zipName)
    {
        $this->zipName = $zipName;
    }

    public function addMedia($mediaItems)
    {
        $this->mediaItems = $mediaItems;

        return $this;
    }

    public function toResponse($request)
    {
        return new StreamedZipResponse(function () {
            $zip = new ZipStream($this->zipName);
            
            $this->mediaItems->each(function (Media $media) use ($zip) {
                $zip->addFileFromStream($media->file_name, $media->stream());
            });
            
            $zip->finish();
        });
    }
}
```

Of course there are situations (eg. when the same assets get downloaded over and over again, or when download speed is very important) where you still want to create a zip file locally and store it for later use. But I believe `StreamedZipResponse` does provide a good solution for the proverbial 80%

Like mentioned above, multi file downloads are coming to [v7 of laravel-medialibrary](https://github.com/spatie/laravel-medialibrary/tree/v7) which will be released around February - March 2018. Of course you can start using [v6 of laravel-medialibrary](https://docs.spatie.be/laravel-medialibrary/v6/) right now.