ux icon indicating copy to clipboard operation
ux copied to clipboard

[LiveComponent] Add support for downloading files from LiveActions (Experimental)

Open smnandre opened this issue 11 months ago β€’ 15 comments

Q A
Bug fix? no
New feature? yes
Issues Close #1516
License MIT

This pull request introduces a new experimental feature for handling file downloads in LiveActions.

[!CAUTION] As stated here, this feature -if merged- will keep an experimental status, for everyone to test and see how it goes. We cannot ensure BC promise on features without testing it IRL first.

TL;DR;

Send file


 #[LiveAction]
public function getDocument(): Response
{
    $file = $this->projectDir . '/docs/legals/terms.pdf');

    return LiveResponse::file($file);
}

Stream files


 #[LiveAction]
public function streamThisReport(): StreamedResponse
{
    // Get a stream, or generate
    $resource =  $this->readStream('file/big/report.csv');

    return LiveResponse::streamFile($resource, 'report.csv');
}

⚠️ We are working to keep state synchronization and stream working together.

Also

I started the demo, just need to complete some texts + add a bit of style

Scope of this

This PR taks a very narrow scope on this thing, i already started working on cool things for upload (as I also needed to for work..)

Let's stay focus on this here (discussions / issues open for any new idea / MR)

smnandre avatar Jan 05 '25 02:01 smnandre

πŸ“Š Packages dist files size difference

Thanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR. Please review the changes and make sure they are expected.

FileBefore (Size / Gzip)After (Size / Gzip)
LiveComponent
Backend/BackendResponse.d.ts 136 B / 139 B 166 B+22% πŸ“ˆ / 148 B+6% πŸ“ˆ
live_controller.js 119.92 kB / 23.37 kB 120.96 kB+1% πŸ“ˆ / 23.6 kB+1% πŸ“ˆ

github-actions[bot] avatar Jan 05 '25 02:01 github-actions[bot]

What about streams from flysystem? We'll need to always load files into memory?

norkunas avatar Jan 06 '25 05:01 norkunas

What about streams from flysystem? We'll need to always load files into memory?

With that method, i fear yes. That's why the limit. And browsers can change their policy soon to prevent abuse.

We also can ask for a Filesystem API grant.

Again, will need to test one way or the other, locally i can stream a full 8GB file in one second so ... :|

smnandre avatar Jan 07 '25 01:01 smnandre

Why aren't you deferring to BinaryFileResponse for this logic?

How so ? BinaryFile does not add content disposition different than what you pass here as argument.

smnandre avatar Jan 08 '25 22:01 smnandre

  1. I wish we weren't restricted to BinaryFileResponse - I'd like to be able to use with StreamedResponse and a resource.

I need to continue looking, but the restriction is not here at all, there is just an helper we can provide to offer the best DX possible.

The difficulty is to ensure a ResponseStream will work (without crashing the LiveComponent thread) and this is where problems come from:

  • unknown size is a bad signal
  • content type must be set and be legit

And regarding streaming. I made it work perfectly at home. But i have still not see any use case where downloading a 500MB file via a LiveComponent is a good idea πŸ˜…

Anyway we'll need to test IRL a bit to get feedback, issues, etc etc

smnandre avatar Jan 08 '25 22:01 smnandre

And regarding streaming. I made it work perfectly at home. But i have still not see any use case where downloading a 500MB file via a LiveComponent is a good idea πŸ˜…

But it's a good idea to reduce resource usage in case memory limit is low.

norkunas avatar Jan 09 '25 04:01 norkunas

Simon, thanks for this nice new feature!

My only concern is about the name of this class:

return new LiveDownloadResponse($file);

LiveDownloadResponse is related to files but its name has nothing to do with files. Also, download is not strictly correct, because we can download files, embed files in the browser and stream files.

Here are some proposals for your consideration:

return new LiveFileResponse($file);
return new FileLiveResponse($file);
return new FileResponse($file);
return new StreamResponse($file);
return new ResourceResponse($file);

javiereguiluz avatar Jan 10 '25 09:01 javiereguiluz

We've been working on it with @kbond and decided not to extend Response at all... (users can use whatever Response they want in term of implementation.

...but we would provide a static helper to create the best DX possible.

    // ... 

    return LiveResponse::file($myFile);
}

That way we can also offer

    // ... 

    return LiveResponse::streamFile($myFile);
}

And this would be perfect for other ideas we have next (no content, refresh...)


We are now trying to see the best way to send files and save component state/update the DOM (not the case in my PR)

smnandre avatar Jan 10 '25 11:01 smnandre

Very nice! Thanks for the insights.

javiereguiluz avatar Jan 10 '25 14:01 javiereguiluz

Would it be possible to use a redirect? I would like to do pre-signed urls and let the download be directly handled by an S3 bucket.

mahono avatar Jan 12 '25 11:01 mahono

Would it be possible to use a redirect? I would like to do pre-signed urls and let the download be directly handled by an S3 bucket.

Yes, that's possible now!

kbond avatar Jan 12 '25 12:01 kbond

Can yield be used/supported instead of echo for streamed response? Asking for a support of running Symfony in worker mode, where echoes are forbidden.

FluffyDiscord avatar Jan 14 '25 07:01 FluffyDiscord

So how do we move on here? @kbond your work on the streams can be added right now ? do we need more tests before opening an alpha/experimental phase ?

smnandre avatar Jan 17 '25 21:01 smnandre

Hi ! Any news on this PR ?

ThibaultJlvt avatar Aug 01 '25 11:08 ThibaultJlvt

Open source has taken a back seat lately, but I hope to clean and merge this soon.

If this feature matters to you or your company, feel free to reach outβ€”either by supporting a contributor through GitHub Sponsors or backing Symfony directly via the official sponsorship page.

Any support is appreciated and helps keep the community moving :)

smnandre avatar Aug 02 '25 02:08 smnandre