123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- <?php
-
- namespace Illuminate\Filesystem;
-
- use RuntimeException;
- use Illuminate\Http\File;
- use Illuminate\Support\Str;
- use InvalidArgumentException;
- use Illuminate\Support\Carbon;
- use Illuminate\Http\UploadedFile;
- use Illuminate\Support\Collection;
- use League\Flysystem\AdapterInterface;
- use PHPUnit\Framework\Assert as PHPUnit;
- use League\Flysystem\FilesystemInterface;
- use League\Flysystem\AwsS3v3\AwsS3Adapter;
- use League\Flysystem\Cached\CachedAdapter;
- use League\Flysystem\FileNotFoundException;
- use League\Flysystem\Rackspace\RackspaceAdapter;
- use League\Flysystem\Adapter\Local as LocalAdapter;
- use Symfony\Component\HttpFoundation\StreamedResponse;
- use Illuminate\Contracts\Filesystem\Cloud as CloudFilesystemContract;
- use Illuminate\Contracts\Filesystem\Filesystem as FilesystemContract;
- use Illuminate\Contracts\Filesystem\FileNotFoundException as ContractFileNotFoundException;
-
- /**
- * @mixin \League\Flysystem\FilesystemInterface
- */
- class FilesystemAdapter implements FilesystemContract, CloudFilesystemContract
- {
- /**
- * The Flysystem filesystem implementation.
- *
- * @var \League\Flysystem\FilesystemInterface
- */
- protected $driver;
-
- /**
- * Create a new filesystem adapter instance.
- *
- * @param \League\Flysystem\FilesystemInterface $driver
- * @return void
- */
- public function __construct(FilesystemInterface $driver)
- {
- $this->driver = $driver;
- }
-
- /**
- * Assert that the given file exists.
- *
- * @param string $path
- * @return void
- */
- public function assertExists($path)
- {
- PHPUnit::assertTrue(
- $this->exists($path), "Unable to find a file at path [{$path}]."
- );
- }
-
- /**
- * Assert that the given file does not exist.
- *
- * @param string $path
- * @return void
- */
- public function assertMissing($path)
- {
- PHPUnit::assertFalse(
- $this->exists($path), "Found unexpected file at path [{$path}]."
- );
- }
-
- /**
- * Determine if a file exists.
- *
- * @param string $path
- * @return bool
- */
- public function exists($path)
- {
- return $this->driver->has($path);
- }
-
- /**
- * Get the full path for the file at the given "short" path.
- *
- * @param string $path
- * @return string
- */
- public function path($path)
- {
- return $this->driver->getAdapter()->getPathPrefix().$path;
- }
-
- /**
- * Get the contents of a file.
- *
- * @param string $path
- * @return string
- *
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
- */
- public function get($path)
- {
- try {
- return $this->driver->read($path);
- } catch (FileNotFoundException $e) {
- throw new ContractFileNotFoundException($path, $e->getCode(), $e);
- }
- }
-
- /**
- * Create a streamed response for a given file.
- *
- * @param string $path
- * @param string|null $name
- * @param array|null $headers
- * @param string|null $disposition
- * @return \Symfony\Component\HttpFoundation\StreamedResponse
- */
- public function response($path, $name = null, array $headers = [], $disposition = 'inline')
- {
- $response = new StreamedResponse;
-
- $disposition = $response->headers->makeDisposition($disposition, $name ?? basename($path));
-
- $response->headers->replace($headers + [
- 'Content-Type' => $this->mimeType($path),
- 'Content-Length' => $this->size($path),
- 'Content-Disposition' => $disposition,
- ]);
-
- $response->setCallback(function () use ($path) {
- $stream = $this->driver->readStream($path);
- fpassthru($stream);
- fclose($stream);
- });
-
- return $response;
- }
-
- /**
- * Create a streamed download response for a given file.
- *
- * @param string $path
- * @param string|null $name
- * @param array|null $headers
- * @return \Symfony\Component\HttpFoundation\StreamedResponse
- */
- public function download($path, $name = null, array $headers = [])
- {
- return $this->response($path, $name, $headers, 'attachment');
- }
-
- /**
- * Write the contents of a file.
- *
- * @param string $path
- * @param string|resource $contents
- * @param mixed $options
- * @return bool
- */
- public function put($path, $contents, $options = [])
- {
- $options = is_string($options)
- ? ['visibility' => $options]
- : (array) $options;
-
- // If the given contents is actually a file or uploaded file instance than we will
- // automatically store the file using a stream. This provides a convenient path
- // for the developer to store streams without managing them manually in code.
- if ($contents instanceof File ||
- $contents instanceof UploadedFile) {
- return $this->putFile($path, $contents, $options);
- }
-
- return is_resource($contents)
- ? $this->driver->putStream($path, $contents, $options)
- : $this->driver->put($path, $contents, $options);
- }
-
- /**
- * Store the uploaded file on the disk.
- *
- * @param string $path
- * @param \Illuminate\Http\File|\Illuminate\Http\UploadedFile $file
- * @param array $options
- * @return string|false
- */
- public function putFile($path, $file, $options = [])
- {
- return $this->putFileAs($path, $file, $file->hashName(), $options);
- }
-
- /**
- * Store the uploaded file on the disk with a given name.
- *
- * @param string $path
- * @param \Illuminate\Http\File|\Illuminate\Http\UploadedFile $file
- * @param string $name
- * @param array $options
- * @return string|false
- */
- public function putFileAs($path, $file, $name, $options = [])
- {
- $stream = fopen($file->getRealPath(), 'r+');
-
- // Next, we will format the path of the file and store the file using a stream since
- // they provide better performance than alternatives. Once we write the file this
- // stream will get closed automatically by us so the developer doesn't have to.
- $result = $this->put(
- $path = trim($path.'/'.$name, '/'), $stream, $options
- );
-
- if (is_resource($stream)) {
- fclose($stream);
- }
-
- return $result ? $path : false;
- }
-
- /**
- * Get the visibility for the given path.
- *
- * @param string $path
- * @return string
- */
- public function getVisibility($path)
- {
- if ($this->driver->getVisibility($path) == AdapterInterface::VISIBILITY_PUBLIC) {
- return FilesystemContract::VISIBILITY_PUBLIC;
- }
-
- return FilesystemContract::VISIBILITY_PRIVATE;
- }
-
- /**
- * Set the visibility for the given path.
- *
- * @param string $path
- * @param string $visibility
- * @return void
- */
- public function setVisibility($path, $visibility)
- {
- return $this->driver->setVisibility($path, $this->parseVisibility($visibility));
- }
-
- /**
- * Prepend to a file.
- *
- * @param string $path
- * @param string $data
- * @param string $separator
- * @return int
- */
- public function prepend($path, $data, $separator = PHP_EOL)
- {
- if ($this->exists($path)) {
- return $this->put($path, $data.$separator.$this->get($path));
- }
-
- return $this->put($path, $data);
- }
-
- /**
- * Append to a file.
- *
- * @param string $path
- * @param string $data
- * @param string $separator
- * @return int
- */
- public function append($path, $data, $separator = PHP_EOL)
- {
- if ($this->exists($path)) {
- return $this->put($path, $this->get($path).$separator.$data);
- }
-
- return $this->put($path, $data);
- }
-
- /**
- * Delete the file at a given path.
- *
- * @param string|array $paths
- * @return bool
- */
- public function delete($paths)
- {
- $paths = is_array($paths) ? $paths : func_get_args();
-
- $success = true;
-
- foreach ($paths as $path) {
- try {
- if (! $this->driver->delete($path)) {
- $success = false;
- }
- } catch (FileNotFoundException $e) {
- $success = false;
- }
- }
-
- return $success;
- }
-
- /**
- * Copy a file to a new location.
- *
- * @param string $from
- * @param string $to
- * @return bool
- */
- public function copy($from, $to)
- {
- return $this->driver->copy($from, $to);
- }
-
- /**
- * Move a file to a new location.
- *
- * @param string $from
- * @param string $to
- * @return bool
- */
- public function move($from, $to)
- {
- return $this->driver->rename($from, $to);
- }
-
- /**
- * Get the file size of a given file.
- *
- * @param string $path
- * @return int
- */
- public function size($path)
- {
- return $this->driver->getSize($path);
- }
-
- /**
- * Get the mime-type of a given file.
- *
- * @param string $path
- * @return string|false
- */
- public function mimeType($path)
- {
- return $this->driver->getMimetype($path);
- }
-
- /**
- * Get the file's last modification time.
- *
- * @param string $path
- * @return int
- */
- public function lastModified($path)
- {
- return $this->driver->getTimestamp($path);
- }
-
- /**
- * Get the URL for the file at the given path.
- *
- * @param string $path
- * @return string
- */
- public function url($path)
- {
- $adapter = $this->driver->getAdapter();
-
- if ($adapter instanceof CachedAdapter) {
- $adapter = $adapter->getAdapter();
- }
-
- if (method_exists($adapter, 'getUrl')) {
- return $adapter->getUrl($path);
- } elseif (method_exists($this->driver, 'getUrl')) {
- return $this->driver->getUrl($path);
- } elseif ($adapter instanceof AwsS3Adapter) {
- return $this->getAwsUrl($adapter, $path);
- } elseif ($adapter instanceof RackspaceAdapter) {
- return $this->getRackspaceUrl($adapter, $path);
- } elseif ($adapter instanceof LocalAdapter) {
- return $this->getLocalUrl($path);
- } else {
- throw new RuntimeException('This driver does not support retrieving URLs.');
- }
- }
-
- /**
- * Get the URL for the file at the given path.
- *
- * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter
- * @param string $path
- * @return string
- */
- protected function getAwsUrl($adapter, $path)
- {
- // If an explicit base URL has been set on the disk configuration then we will use
- // it as the base URL instead of the default path. This allows the developer to
- // have full control over the base path for this filesystem's generated URLs.
- if (! is_null($url = $this->driver->getConfig()->get('url'))) {
- return $this->concatPathToUrl($url, $adapter->getPathPrefix().$path);
- }
-
- return $adapter->getClient()->getObjectUrl(
- $adapter->getBucket(), $adapter->getPathPrefix().$path
- );
- }
-
- /**
- * Get the URL for the file at the given path.
- *
- * @param \League\Flysystem\Rackspace\RackspaceAdapter $adapter
- * @param string $path
- * @return string
- */
- protected function getRackspaceUrl($adapter, $path)
- {
- return (string) $adapter->getContainer()->getObject($path)->getPublicUrl();
- }
-
- /**
- * Get the URL for the file at the given path.
- *
- * @param string $path
- * @return string
- */
- protected function getLocalUrl($path)
- {
- $config = $this->driver->getConfig();
-
- // If an explicit base URL has been set on the disk configuration then we will use
- // it as the base URL instead of the default path. This allows the developer to
- // have full control over the base path for this filesystem's generated URLs.
- if ($config->has('url')) {
- return $this->concatPathToUrl($config->get('url'), $path);
- }
-
- $path = '/storage/'.$path;
-
- // If the path contains "storage/public", it probably means the developer is using
- // the default disk to generate the path instead of the "public" disk like they
- // are really supposed to use. We will remove the public from this path here.
- if (Str::contains($path, '/storage/public/')) {
- return Str::replaceFirst('/public/', '/', $path);
- }
-
- return $path;
- }
-
- /**
- * Get a temporary URL for the file at the given path.
- *
- * @param string $path
- * @param \DateTimeInterface $expiration
- * @param array $options
- * @return string
- */
- public function temporaryUrl($path, $expiration, array $options = [])
- {
- $adapter = $this->driver->getAdapter();
-
- if ($adapter instanceof CachedAdapter) {
- $adapter = $adapter->getAdapter();
- }
-
- if (method_exists($adapter, 'getTemporaryUrl')) {
- return $adapter->getTemporaryUrl($path, $expiration, $options);
- } elseif ($adapter instanceof AwsS3Adapter) {
- return $this->getAwsTemporaryUrl($adapter, $path, $expiration, $options);
- } elseif ($adapter instanceof RackspaceAdapter) {
- return $this->getRackspaceTemporaryUrl($adapter, $path, $expiration, $options);
- } else {
- throw new RuntimeException('This driver does not support creating temporary URLs.');
- }
- }
-
- /**
- * Get a temporary URL for the file at the given path.
- *
- * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter
- * @param string $path
- * @param \DateTimeInterface $expiration
- * @param array $options
- * @return string
- */
- public function getAwsTemporaryUrl($adapter, $path, $expiration, $options)
- {
- $client = $adapter->getClient();
-
- $command = $client->getCommand('GetObject', array_merge([
- 'Bucket' => $adapter->getBucket(),
- 'Key' => $adapter->getPathPrefix().$path,
- ], $options));
-
- return (string) $client->createPresignedRequest(
- $command, $expiration
- )->getUri();
- }
-
- /**
- * Get a temporary URL for the file at the given path.
- *
- * @param \League\Flysystem\Rackspace\RackspaceAdapter $adapter
- * @param string $path
- * @param \DateTimeInterface $expiration
- * @param array $options
- * @return string
- */
- public function getRackspaceTemporaryUrl($adapter, $path, $expiration, $options)
- {
- return $adapter->getContainer()->getObject($path)->getTemporaryUrl(
- Carbon::now()->diffInSeconds($expiration),
- $options['method'] ?? 'GET',
- $options['forcePublicUrl'] ?? true
- );
- }
-
- /**
- * Concatenate a path to a URL.
- *
- * @param string $url
- * @param string $path
- * @return string
- */
- protected function concatPathToUrl($url, $path)
- {
- return rtrim($url, '/').'/'.ltrim($path, '/');
- }
-
- /**
- * Get an array of all files in a directory.
- *
- * @param string|null $directory
- * @param bool $recursive
- * @return array
- */
- public function files($directory = null, $recursive = false)
- {
- $contents = $this->driver->listContents($directory, $recursive);
-
- return $this->filterContentsByType($contents, 'file');
- }
-
- /**
- * Get all of the files from the given directory (recursive).
- *
- * @param string|null $directory
- * @return array
- */
- public function allFiles($directory = null)
- {
- return $this->files($directory, true);
- }
-
- /**
- * Get all of the directories within a given directory.
- *
- * @param string|null $directory
- * @param bool $recursive
- * @return array
- */
- public function directories($directory = null, $recursive = false)
- {
- $contents = $this->driver->listContents($directory, $recursive);
-
- return $this->filterContentsByType($contents, 'dir');
- }
-
- /**
- * Get all (recursive) of the directories within a given directory.
- *
- * @param string|null $directory
- * @return array
- */
- public function allDirectories($directory = null)
- {
- return $this->directories($directory, true);
- }
-
- /**
- * Create a directory.
- *
- * @param string $path
- * @return bool
- */
- public function makeDirectory($path)
- {
- return $this->driver->createDir($path);
- }
-
- /**
- * Recursively delete a directory.
- *
- * @param string $directory
- * @return bool
- */
- public function deleteDirectory($directory)
- {
- return $this->driver->deleteDir($directory);
- }
-
- /**
- * Flush the Flysystem cache.
- *
- * @return void
- */
- public function flushCache()
- {
- $adapter = $this->driver->getAdapter();
-
- if ($adapter instanceof CachedAdapter) {
- $adapter->getCache()->flush();
- }
- }
-
- /**
- * Get the Flysystem driver.
- *
- * @return \League\Flysystem\FilesystemInterface
- */
- public function getDriver()
- {
- return $this->driver;
- }
-
- /**
- * Filter directory contents by type.
- *
- * @param array $contents
- * @param string $type
- * @return array
- */
- protected function filterContentsByType($contents, $type)
- {
- return Collection::make($contents)
- ->where('type', $type)
- ->pluck('path')
- ->values()
- ->all();
- }
-
- /**
- * Parse the given visibility value.
- *
- * @param string|null $visibility
- * @return string|null
- *
- * @throws \InvalidArgumentException
- */
- protected function parseVisibility($visibility)
- {
- if (is_null($visibility)) {
- return;
- }
-
- switch ($visibility) {
- case FilesystemContract::VISIBILITY_PUBLIC:
- return AdapterInterface::VISIBILITY_PUBLIC;
- case FilesystemContract::VISIBILITY_PRIVATE:
- return AdapterInterface::VISIBILITY_PRIVATE;
- }
-
- throw new InvalidArgumentException("Unknown visibility: {$visibility}");
- }
-
- /**
- * Pass dynamic methods call onto Flysystem.
- *
- * @param string $method
- * @param array $parameters
- * @return mixed
- *
- * @throws \BadMethodCallException
- */
- public function __call($method, array $parameters)
- {
- return call_user_func_array([$this->driver, $method], $parameters);
- }
- }
|