diff --git a/src/Illuminate/Filesystem/FilesystemAdapter.php b/src/Illuminate/Filesystem/FilesystemAdapter.php index 50ce21f3671d..7c153225b0d6 100644 --- a/src/Illuminate/Filesystem/FilesystemAdapter.php +++ b/src/Illuminate/Filesystem/FilesystemAdapter.php @@ -793,6 +793,16 @@ public function providesTemporaryUrls() return method_exists($this->adapter, 'getTemporaryUrl') || isset($this->temporaryUrlCallback); } + /** + * Determine if streamable URIs can be generated. + * + * @return bool + */ + public function providesStreamableUris() + { + return isset($this->config['stream_wrapper']); + } + /** * Get a temporary URL for the file at the given path. * @@ -837,6 +847,32 @@ public function temporaryUploadUrl($path, $expiration, array $options = []) throw new RuntimeException('This driver does not support creating temporary upload URLs.'); } + /** + * Get a stream URI for the file at the given path. + * + * @param string $path + * @param array $options + * @return string + * + * @throws \RuntimeException + */ + public function streamableUri($path, array $options = []) + { + if (! $this->providesStreamableUris()) { + throw new RuntimeException('This driver does not support creating stream URIs.'); + } + + $wrapper = $this->config['stream_wrapper']; + + $availableWrappers = stream_get_wrappers(); + + if (! in_array($wrapper, $availableWrappers)) { + throw new RuntimeException("The stream wrapper [{$wrapper}] is not available."); + } + + return $wrapper.'://'.ltrim($this->path($path), '\\/'); + } + /** * Concatenate a path to a URL. * diff --git a/tests/Filesystem/FilesystemAdapterTest.php b/tests/Filesystem/FilesystemAdapterTest.php index 9adba384baa0..59994248692d 100644 --- a/tests/Filesystem/FilesystemAdapterTest.php +++ b/tests/Filesystem/FilesystemAdapterTest.php @@ -21,6 +21,7 @@ use Mockery as m; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; +use RuntimeException; use Symfony\Component\HttpFoundation\StreamedResponse; class FilesystemAdapterTest extends TestCase @@ -737,4 +738,71 @@ public function testGetChecksum() $this->assertEquals('730bed78bccf58c2cfe44c29b71e5e6b', $filesystemAdapter->checksum('path.txt')); $this->assertEquals('a5c3556d', $filesystemAdapter->checksum('path.txt', ['checksum_algo' => 'crc32'])); } + + public function testProvidesStreamableUris() + { + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter, [ + 'stream_wrapper' => 'file', + ]); + + $this->assertTrue($filesystemAdapter->providesStreamableUris()); + } + + public function testDoesNotProvideStreamableUrisWhenConfigurationIsMissing() + { + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + + $this->assertFalse($filesystemAdapter->providesStreamableUris()); + } + + public function testStreamableUri() + { + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter, [ + 'root' => $this->tempDir.DIRECTORY_SEPARATOR, + 'stream_wrapper' => 'file', + ]); + + $filesystemAdapter->write('file.txt', 'contents of file'); + + $this->assertEquals('file://'.ltrim($this->tempDir, '/').DIRECTORY_SEPARATOR.'file.txt', $filesystemAdapter->streamableUri('file.txt')); + } + + public function testStreamableUriWithPrefix() + { + $prefix = 'images'.DIRECTORY_SEPARATOR; + + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter, [ + 'stream_wrapper' => 'file', + 'root' => $this->tempDir.DIRECTORY_SEPARATOR, + 'prefix' => $prefix, + ]); + + $filesystemAdapter->write('file.txt', 'contents of file'); + + $this->assertEquals('file://'.ltrim($this->tempDir, '/').DIRECTORY_SEPARATOR.$prefix.'file.txt', $filesystemAdapter->streamableUri('file.txt')); + } + + public function testThrowExceptionsForStramableUri() + { + $this->expectException(RuntimeException::class); + + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + + $filesystemAdapter->write('file.txt', 'contents of file'); + + $filesystemAdapter->streamableUri('file.txt'); + } + + public function testThrowExceptionsWhenStreamWrapperIsNotSupported() + { + $this->expectException(RuntimeException::class); + + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter, [ + 'stream_wrapper' => 'wrapper_that_does_not_exist', + ]); + + $filesystemAdapter->write('file.txt', 'contents of file'); + + $filesystemAdapter->streamableUri('file.txt'); + } }