-
-
Notifications
You must be signed in to change notification settings - Fork 332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[LiveComponent] Add support for downloading files from LiveActions (Experimental) #2483
base: 2.x
Are you sure you want to change the base?
Conversation
📊 Packages dist files size differenceThanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This solution is so simple 🤩
src/LiveComponent/src/EventListener/LiveComponentSubscriber.php
Outdated
Show resolved
Hide resolved
parent::__construct($file, 200, [ | ||
self::HEADER_LIVE_DOWNLOAD => 1, | ||
'Content-Disposition' => HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $filename ?? basename($file)), | ||
'Content-Type' => 'application/octet-stream', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we use the $file
's mime type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hesitated (keep dep light) and chose not to...
But in fact it's a good idea and Mime is a very small component.
So let's make it a requirement for LiveComponent ? Or only when using downloads / uploads ?
wdyt ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to put it in suggest
composer.json
section (I don't remember what is the rule in Symfony, cc @nicolas-grekas), and then runtime check if symfony/mime
exists (when using downloads/uploads)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure there is no suggest in Symfony composer packages..
We have other situations in UX where we do not require a package (i.e. in Autocomplete for Form ..)
My question was more: should we require it for everyone, or keep it as an "runtime" dependency ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runtime dependency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why aren't you deferring to BinaryFileResponse for this logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for the header?
In the frontend, if not a standard/known content type, but has a content disposition (and an other requirements), trigger the download? This way we could say "to enable file downloads, return a response that has a content disposition"
Co-authored-by: Hugo Alliaume <[email protected]>
Co-authored-by: Hugo Alliaume <[email protected]>
Co-authored-by: Hugo Alliaume <[email protected]>
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 ... :| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome! I'll let someone else review the frontend code.
For the back end:
- I wish we weren't restricted to
BinaryFileResponse
- I'd like to be able to use with StreamedResponse and a resource. - What are the requirements for a response to trigger as a download on the frontend? Just the special header? Could we add this header in the response event listener (detect if it's a download)?
parent::__construct($file, 200, [ | ||
self::HEADER_LIVE_DOWNLOAD => 1, | ||
'Content-Disposition' => HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $filename ?? basename($file)), | ||
'Content-Type' => 'application/octet-stream', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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:
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 |
throw new Error('No filename found in Content-Disposition header'); | ||
} | ||
|
||
const blob = await backendResponse.getBlob(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends... if you trigger the "click" and the file start downloading I think browser play the "pipe" role here.
But during my test I managed to crashed pretty violently Chrome and Safari multiple times.
But it's a good idea to reduce resource usage in case memory limit is low. |
Simon, thanks for this nice new feature! My only concern is about the name of this class: return new LiveDownloadResponse($file);
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); |
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) |
Very nice! Thanks for the insights. |
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! |
Can |
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 ? |
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
Stream files
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)