filamentphp / filament

A collection of beautiful full-stack components for Laravel. The perfect starting point for your next app. Using Livewire, Alpine.js and Tailwind CSS.
https://filamentphp.com
MIT License
19.47k stars 2.98k forks source link

Support non-streamed downloads #14591

Open Robertbaelde opened 1 month ago

Robertbaelde commented 1 month ago

Description

Some hosting environments do not support streamed downloads (mainly environments running on lambda like Laravel Vapor or Bref). This makes the export functionality not work because the downloaders assume we can do a streamed download.

This PR adds a config to enable/disable streamed downloads. When disabled, the downloader will write the contents to one file and uploads it to the disk (so we don't have to re-do the generation on multiple downloads).

Visual changes

N/A

Functional changes

austincarpenter commented 1 month ago

To get things working on Vapor, you need to add the X-Vapor-Base64-Encode header in the response. (See https://docs.vapor.build/projects/development.html#binary-responses)

I only use the XLSX downloader so I've simply subclassed XlsxDownloader, replacing the __invoke() function to add the header:

public function __invoke(Export $export): StreamedResponse
{
    return tap(parent::__invoke($export), function (StreamedResponse $response) {
        $response->headers->set('X-Vapor-Base64-Encode', true);
    });
}
$this->app->bind(BaseXlsxDownloader::class, XlsxDownloader::class);
danharrin commented 1 month ago

Is there a way we can detect Vapor within Filament and do this on the users behalf? Instead of disabling streaming?

Robertbaelde commented 1 month ago

Is there a way we can detect Vapor within Filament and do this on the users behalf? Instead of disabling streaming?

For Vapor: isset($_ENV['VAPOR_SSM_PATH']) || isset($_SERVER['VAPOR_SSM_PATH']) For Bref: env('LAMBDA_TASK_ROOT') (detects Lambda runtime) or env('BREF_LOOP_MAX')

Will try to reshape this PR a bit with the given feedback. I'm afraid for Bref downloads larger than 6MB should come from a disk (s3), see: https://bref.sh/docs/use-cases/http/binary-requests-responses

~Not sure if/how Vapor mitigates that though~ Edit: Vapor also has the 6MB file limit