-
-
Notifications
You must be signed in to change notification settings - Fork 340
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
138 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace Symfony\UX\LiveComponent; | ||
|
||
use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||
use Symfony\Component\HttpFoundation\HeaderUtils; | ||
use Symfony\Component\HttpFoundation\StreamedResponse; | ||
|
||
/** | ||
* @author Simon André <[email protected]> | ||
* @author Kevin Bond <[email protected]> | ||
*/ | ||
final class LiveResponse | ||
{ | ||
/** | ||
* @param string|\SplFileInfo $file The file to send as a response | ||
* @param string|null $filename The name of the file to send (defaults to the basename of the file) | ||
* @param string|null $contentType The content type of the file (defaults to `application/octet-stream`) | ||
*/ | ||
public static function file(string|\SplFileInfo $file, ?string $filename = null, ?string $contentType = null, ?int $size = null): BinaryFileResponse | ||
{ | ||
return new BinaryFileResponse($file, 200, [ | ||
'Content-Disposition' => HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $filename ?? basename($file)), | ||
'Content-Type' => $contentType ?? 'application/octet-stream', | ||
'Content-Length' => $size ?? ($file instanceof \SplFileInfo ? $file->getSize() : null), | ||
]); | ||
} | ||
|
||
/** | ||
* @param resource|Closure $file The file to stream as a response | ||
* @param string $filename The name of the file to send (defaults to the basename of the file) | ||
* @param string|null $contentType The content type of the file (defaults to `application/octet-stream`) | ||
* @param int|null $size The size of the file | ||
*/ | ||
public static function streamFile(mixed $file, string $filename, ?string $contentType = null, ?int $size = null): StreamedResponse | ||
{ | ||
if (!is_resource($file) && !$file instanceof \Closure) { | ||
throw new \InvalidArgumentException(sprintf('The file must be a resource or a closure, "%s" given.', get_debug_type($file))); | ||
} | ||
|
||
return new StreamedResponse($file instanceof \Closure ? $file(...) : function () use ($file) { | ||
while (!feof($file)) { | ||
echo fread($file, 1024); | ||
} | ||
}, 200, [ | ||
'Content-Disposition' => HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $filename), | ||
'Content-Type' => $contentType ?? 'application/octet-stream', | ||
'Content-Length' => $size, | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ | |
use Symfony\UX\LiveComponent\Attribute\LiveAction; | ||
use Symfony\UX\LiveComponent\Attribute\LiveArg; | ||
use Symfony\UX\LiveComponent\DefaultActionTrait; | ||
use Symfony\UX\LiveComponent\LiveDownloadResponse; | ||
use Symfony\UX\LiveComponent\LiveResponse; | ||
|
||
/** | ||
* @author Simon André <[email protected]> | ||
|
@@ -25,32 +25,32 @@ | |
class DownloadFileComponent | ||
{ | ||
use DefaultActionTrait; | ||
private const FILE_DIRECTORY = __DIR__.'/../files/'; | ||
|
||
private const FILE_DIRECTORY = __DIR__.'/../files/'; | ||
|
||
#[LiveAction] | ||
public function download(): BinaryFileResponse | ||
{ | ||
$file = new \SplFileInfo(self::FILE_DIRECTORY.'/foo.json'); | ||
return new LiveDownloadResponse($file); | ||
|
||
return LiveResponse::file($file); | ||
} | ||
|
||
#[LiveAction] | ||
public function generate(): BinaryFileResponse | ||
{ | ||
$file = new \SplTempFileObject(); | ||
$file->fwrite(file_get_contents(self::FILE_DIRECTORY.'/foo.json')); | ||
return new LiveDownloadResponse($file, 'foo.json'); | ||
|
||
return LiveResponse::file($file, 'foo.json', size: 1000); | ||
} | ||
|
||
#[LiveAction] | ||
public function heavyFile(#[LiveArg] int $size): BinaryFileResponse | ||
{ | ||
$file = new \SplFileInfo(self::FILE_DIRECTORY.'heavy.txt'); | ||
$response = new BinaryFileResponse($file); | ||
|
||
$response = LiveResponse::file($file); | ||
$response->headers->set('Content-Length', 10000000); // 10MB | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
namespace Symfony\UX\LiveComponent\Tests\Unit; | ||
|
||
use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||
use Symfony\Component\HttpFoundation\File\File; | ||
use Symfony\Component\HttpFoundation\StreamedResponse; | ||
use Symfony\UX\LiveComponent\LiveResponse; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class LiveResponseTest extends TestCase | ||
{ | ||
public function testSendFileWithStringPath(): void | ||
{ | ||
$filePath = __DIR__.'/../fixtures/files/test.txt'; | ||
$response = LiveResponse::file($filePath); | ||
|
||
$this->assertInstanceOf(BinaryFileResponse::class, $response); | ||
$this->assertEquals('attachment; filename=test.txt', $response->headers->get('Content-Disposition')); | ||
$this->assertEquals('application/octet-stream', $response->headers->get('Content-Type')); | ||
} | ||
|
||
public function testSendFileWithSplFileInfo(): void | ||
{ | ||
$file = new File(__DIR__.'/../fixtures/files/test.txt'); | ||
$response = LiveResponse::file($file, 'custom-name.txt', 'text/plain'); | ||
|
||
$this->assertInstanceOf(BinaryFileResponse::class, $response); | ||
$this->assertEquals('attachment; filename=custom-name.txt', $response->headers->get('Content-Disposition')); | ||
$this->assertEquals('text/plain', $response->headers->get('Content-Type')); | ||
} | ||
|
||
public function testSendFileWithSplTempFileObject(): void | ||
{ | ||
$tempFile = new \SplTempFileObject(); | ||
$tempFile->fwrite('Temporary content'); | ||
$response = LiveResponse::file($tempFile, size: 17); | ||
|
||
$this->assertInstanceOf(BinaryFileResponse::class, $response); | ||
$this->assertEquals('application/octet-stream', $response->headers->get('Content-Type')); | ||
$this->assertEquals(17, $response->headers->get('Content-Length')); | ||
} | ||
|
||
public function testStreamFileWithResource(): void | ||
{ | ||
$file = fopen(__DIR__.'/../fixtures/files/test.txt', 'rb'); | ||
$response = LiveResponse::streamFile($file, 'streamed-file.txt'); | ||
|
||
$this->assertInstanceOf(StreamedResponse::class, $response); | ||
$this->assertEquals('attachment; filename=streamed-file.txt', $response->headers->get('Content-Disposition')); | ||
$this->assertEquals('application/octet-stream', $response->headers->get('Content-Type')); | ||
fclose($file); | ||
} | ||
|
||
public function testStreamFileWithClosure(): void | ||
{ | ||
$closure = function () { | ||
echo 'Streaming content'; | ||
}; | ||
|
||
$response = LiveResponse::streamFile($closure, 'streamed-closure.txt', 'text/plain'); | ||
|
||
$this->assertInstanceOf(StreamedResponse::class, $response); | ||
$this->assertEquals('attachment; filename=streamed-closure.txt', $response->headers->get('Content-Disposition')); | ||
$this->assertEquals('text/plain', $response->headers->get('Content-Type')); | ||
} | ||
|
||
public function testStreamFileWithInvalidType(): void | ||
{ | ||
$this->expectException(\InvalidArgumentException::class); | ||
$this->expectExceptionMessage('The file must be a resource or a closure, "string" given.'); | ||
|
||
LiveResponse::streamFile('invalid-type', 'invalid.txt'); | ||
} | ||
} |