From be5cb5ad25214ed62b4eec24fd64d714983daea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Thu, 5 Mar 2020 22:55:32 +0100 Subject: [PATCH 01/11] Add Resumable.js driver --- README.md | 23 +- config/chunk-uploader.php | 47 +++- src/Driver/BlueimpUploadDriver.php | 13 +- src/Driver/DropzoneUploadDriver.php | 8 +- src/Driver/MonolithUploadDriver.php | 1 - src/Driver/ResumableJsUploadDriver.php | 200 ++++++++++++++ src/Driver/UploadDriver.php | 2 +- src/Helper/ChunkHelpers.php | 79 +++++- src/Range/RequestBodyRange.php | 15 +- src/UploadManager.php | 6 + tests/Driver/ResumableJsUploadDriverTest.php | 270 +++++++++++++++++++ 11 files changed, 628 insertions(+), 36 deletions(-) create mode 100644 src/Driver/ResumableJsUploadDriver.php create mode 100644 tests/Driver/ResumableJsUploadDriverTest.php diff --git a/README.md b/README.md index 5361b31..2707588 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ project at the moment is [tus](https://tus.io/). - [Monolith](#monolith-driver) - [Blueimp](#blueimp-driver) - [DropzoneJS](#dropzonejs-driver) + - [Resumable.js](#resumable-js-driver) - [Identifiers](#identifiers) - [Session identifier](#session-identifier) - [Contribution](#contribution) @@ -72,7 +73,7 @@ class MyController extends Controller { public function myFunction(Request $request, UploadHandler $handler) { - $handler->handle($request); + return $handler->handle($request); } } ``` @@ -86,8 +87,7 @@ class MyController extends Controller public function myFunction(Request $request) { $handler = app()->make(UploadHandler::class); - $handler->handle($request); - } + return $handler->handle($request);} } ``` @@ -142,11 +142,12 @@ If you wrote a custom driver that others might find useful, please consider addi Below is a list of available drivers along with their individual specs: -Service | Driver name | Chunk upload | Resumable ----------------------------------|-------------|--------------|----------- -[Monolith](#monolith-driver) | `monolith` | no | no -[Blueimp](#blueimp-driver) | `blueimp` | yes | yes -[DropzoneJS](#dropzonejs-driver) | `dropzone` | yes | no +Service | Driver name | Chunk upload | Resumable +-------------------------------------|----------------|--------------|----------- +[Monolith](#monolith-driver) | `monolith` | no | no +[Blueimp](#blueimp-driver) | `blueimp` | yes | yes +[DropzoneJS](#dropzonejs-driver) | `dropzone` | yes | no +[Resumable.js](#resumable-js-driver) | `resumable-js` | yes | yes ### Monolith driver @@ -164,6 +165,12 @@ This driver handles requests made by the Blueimp jQuery File Upload client libra This driver handles requests made by the DropzoneJS client library. +### Resumable.js driver + +[website](http://resumablejs.com/) + +This driver handles requests made by the Resumable.js client library. + ## Identifiers In some cases an identifier is needed for the uploaded file when the client side library does not provide one. diff --git a/config/chunk-uploader.php b/config/chunk-uploader.php index fb31f8a..650dcbd 100644 --- a/config/chunk-uploader.php +++ b/config/chunk-uploader.php @@ -12,7 +12,7 @@ | throughout your application here. By default, the module is setup for | monolith upload. | - | Supported: "monolith", "blueimp", "dropzone" + | Supported: "monolith", "blueimp", "dropzone", "resumable-js" | */ @@ -118,4 +118,49 @@ ], + /* + |-------------------------------------------------------------------------- + | Resumable.js Options + |-------------------------------------------------------------------------- + | + | Here you may configure the options for the Resumable.js driver. + | + */ + + 'resumable-js' => [ + + // The name of the multipart request parameter to use for the file chunk + 'param' => 'file', + + // HTTP method for chunk test request. + 'test-method' => \Illuminate\Http\Request::METHOD_GET, + // HTTP method to use when sending chunks to the server (POST, PUT, PATCH). + 'upload-method' => \Illuminate\Http\Request::METHOD_POST, + + // Extra prefix added before the name of each parameter included in the multipart POST or in the test GET. + 'parameter-namespace' => '', + + 'parameter-names' => [ + // The name of the chunk index (base-1) in the current upload POST parameter to use for the file chunk. + 'chunk-number' => 'resumableChunkNumber', + // The name of the total number of chunks POST parameter to use for the file chunk. + 'total-chunks' => 'resumableTotalChunks', + // The name of the general chunk size POST parameter to use for the file chunk. + 'chunk-size' => 'resumableChunkSize', + // The name of the total file size number POST parameter to use for the file chunk. + 'total-size' => 'resumableTotalSize', + // The name of the unique identifier POST parameter to use for the file chunk. + 'identifier' => 'resumableIdentifier', + // The name of the original file name POST parameter to use for the file chunk. + 'file-name' => 'resumableFilename', + // The name of the file's relative path POST parameter to use for the file chunk. + 'relative-path' => 'resumableRelativePath', + // The name of the current chunk size POST parameter to use for the file chunk. + 'current-chunk-size' => 'resumableCurrentChunkSize', + // The name of the file type POST parameter to use for the file chunk. + 'type' => 'resumableType', + ], + + ], + ]; diff --git a/src/Driver/BlueimpUploadDriver.php b/src/Driver/BlueimpUploadDriver.php index 01f5ab2..5725248 100644 --- a/src/Driver/BlueimpUploadDriver.php +++ b/src/Driver/BlueimpUploadDriver.php @@ -93,18 +93,16 @@ public function download(Request $request, StorageConfig $config) return $this->fileResponse($filename, $config); } + $request->validate([$this->fileParam => 'required']); $filename = $request->query($this->fileParam); - $directory = $config->getChunkDirectory() . '/' . $filename; - /** @var \Illuminate\Filesystem\FilesystemAdapter $disk */ - $disk = Storage::disk($config->getDisk()); - if (! $disk->exists($directory)) { + if (! $this->chunkExists($config, $filename)) { return new JsonResponse([ 'file' => null, ]); } - $chunk = Arr::last($disk->files($directory)); + $chunk = Arr::last($this->chunks($config, $filename)); $size = explode('-', basename($chunk))[1] + 1; return new JsonResponse([ @@ -117,7 +115,6 @@ public function download(Request $request, StorageConfig $config) /** * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier * @param StorageConfig $config * @param \Closure|null $fileUploaded * @@ -149,8 +146,8 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa $path = $this->mergeChunks($config, $chunks, $filename); - if (! empty($config->sweep())) { - Storage::disk($config->getDisk())->deleteDirectory($filename); + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $filename); } $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); diff --git a/src/Driver/DropzoneUploadDriver.php b/src/Driver/DropzoneUploadDriver.php index cbdaa67..63202ed 100644 --- a/src/Driver/DropzoneUploadDriver.php +++ b/src/Driver/DropzoneUploadDriver.php @@ -5,10 +5,8 @@ use Closure; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; -use Illuminate\Support\Facades\Storage; use InvalidArgumentException; use LaraCrafts\ChunkUploader\Helper\ChunkHelpers; -use LaraCrafts\ChunkUploader\Identifier\Identifier; use LaraCrafts\ChunkUploader\Range\RequestBodyRange; use LaraCrafts\ChunkUploader\Response\PercentageJsonResponse; use LaraCrafts\ChunkUploader\StorageConfig; @@ -46,7 +44,6 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl /** * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier * @param StorageConfig $config * @param \Closure|null $fileUploaded * @@ -99,7 +96,6 @@ private function validateChunkRequest(Request $request) /** * @param UploadedFile $file - * @param Identifier $identifier * @param StorageConfig $config * @param \Closure|null $fileUploaded * @@ -153,8 +149,8 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ $path = $this->mergeChunks($config, $chunks, $filename); - if (!empty($config->sweep())) { - Storage::disk($config->getDisk())->deleteDirectory($filename); + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $filename); } $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); diff --git a/src/Driver/MonolithUploadDriver.php b/src/Driver/MonolithUploadDriver.php index b01f562..fbe340f 100644 --- a/src/Driver/MonolithUploadDriver.php +++ b/src/Driver/MonolithUploadDriver.php @@ -48,7 +48,6 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl /** * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php new file mode 100644 index 0000000..d66722e --- /dev/null +++ b/src/Driver/ResumableJsUploadDriver.php @@ -0,0 +1,200 @@ +fileParam = $config['param']; + + $this->uploadMethod = $config['upload-method']; + $this->testMethod = $config['test-method']; + + $this->parameterNamespace = $config['parameter-namespace']; + $this->parameterNames = $config['parameter-names']; + } + + /** + * @inheritDoc + */ + public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + { + if ($this->isRequestMethodIn($request, [$this->testMethod])) { + return $this->resume($request, $config); + } + + if ($this->isRequestMethodIn($request, [$this->uploadMethod])) { + return $this->save($request, $config, $fileUploaded); + } + + throw new MethodNotAllowedHttpException([ + $this->uploadMethod, + $this->testMethod, + ]); + } + + /** + * @param \Illuminate\Http\Request $request + * @param StorageConfig $config + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function resume(Request $request, StorageConfig $config): Response + { + $this->validateChunkRequest($request); + + try { + $range = new RequestBodyRange( + $request->query, + $this->buildParameterName('chunk-number'), + $this->buildParameterName('total-chunks'), + $this->buildParameterName('chunk-size'), + $this->buildParameterName('total-size'), + 1 + ); + } catch (InvalidArgumentException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + $filename = $request->query($this->buildParameterName('identifier')); + $chunkname = $this->buildChunkname($range); + + if (! $this->chunkExists($config, $filename, $chunkname)) { + throw new NotFoundHttpException(); + } + + return new JsonResponse(['OK']); + } + + /** + * @param \Illuminate\Http\Request $request + * @param StorageConfig $config + * @param \Closure|null $fileUploaded + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + { + $file = $request->file($this->fileParam); + + $this->validateUploadedFile($file); + + $this->validateChunkRequest($request); + + return $this->saveChunk($file, $request, $config, $fileUploaded); + } + + /** + * @param Request $request + */ + private function validateChunkRequest(Request $request) + { + $validation = []; + + foreach ($this->parameterNames as $key => $_) { + $validation[$this->buildParameterName($key)] = 'required'; + } + + $request->validate($validation); + } + + /** + * @param UploadedFile $file + * @param Request $request + * @param StorageConfig $config + * @param \Closure|null $fileUploaded + * + * @return Response + */ + private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + { + try { + $range = new RequestBodyRange( + $request, + $this->buildParameterName('chunk-number'), + $this->buildParameterName('total-chunks'), + $this->buildParameterName('chunk-size'), + $this->buildParameterName('total-size'), + 1 + ); + } catch (InvalidArgumentException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + $filename = $request->post($this->buildParameterName('identifier')); + + $chunks = $this->storeChunk($config, $range, $file, $filename); + + if (!$range->isFinished($chunks)) { + return new PercentageJsonResponse($range->getPercentage($chunks)); + } + + $targetFilename = $filename; + + // On windows you can not create a file whose name ends with a dot + if ($file->getClientOriginalExtension()) { + $targetFilename .= '.' . $file->getClientOriginalExtension(); + } + + $path = $this->mergeChunks($config, $chunks, $targetFilename); + + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $filename); + } + + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + + return new PercentageJsonResponse(100); + } + + private function buildParameterName($key) + { + if (! array_key_exists($key, $this->parameterNames)) { + throw new InvalidArgumentException(sprintf('`%s` is an invalid key for parameter name', $key)); + } + + return $this->parameterNamespace . $this->parameterNames[$key]; + } +} diff --git a/src/Driver/UploadDriver.php b/src/Driver/UploadDriver.php index edbbda3..9113a73 100644 --- a/src/Driver/UploadDriver.php +++ b/src/Driver/UploadDriver.php @@ -30,7 +30,7 @@ abstract public function handle(Request $request, StorageConfig $config, Closure /** * @param string $filename - * @param array $config + * @param StorageConfig $config * * @return \Symfony\Component\HttpFoundation\Response */ diff --git a/src/Helper/ChunkHelpers.php b/src/Helper/ChunkHelpers.php index 3e96f95..6ffc594 100644 --- a/src/Helper/ChunkHelpers.php +++ b/src/Helper/ChunkHelpers.php @@ -16,11 +16,13 @@ trait ChunkHelpers protected $bufferSize = 4096; /** + * Combine all the given chunks to one single file with the given filename. + * * @param StorageConfig $config * @param array $chunks * @param string $targetFilename * - * @return string + * @return string The path of the created file. */ public function mergeChunks(StorageConfig $config, array $chunks, string $targetFilename): string { @@ -47,19 +49,29 @@ public function mergeChunks(StorageConfig $config, array $chunks, string $target } /** + * Delete a directory with the given name from the chunk directory. + * + * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param string $uuid + */ + public function deleteChunkDirectory(StorageConfig $config, string $uuid): void + { + $directory = $config->getChunkDirectory() . '/' . $uuid; + Storage::disk($config->getDisk())->deleteDirectory($directory); + } + + /** + * Persist an uploaded chunk in a directory with the given name in the chunk directory. + * * @param StorageConfig $config * @param Range $range * @param UploadedFile $file * @param string $uuid * @return array */ - public function storeChunk(StorageConfig $config, Range $range, UploadedFile $file, string $uuid) + public function storeChunk(StorageConfig $config, Range $range, UploadedFile $file, string $uuid): array { - $len = strlen($range->getTotal()); - $chunkname = implode('-', [ - str_pad($range->getStart(), $len, '0', STR_PAD_LEFT), - str_pad($range->getEnd(), $len, '0', STR_PAD_LEFT), - ]); + $chunkname = $this->buildChunkname($range); $directory = $config->getChunkDirectory() . '/' . $uuid; $file->storeAs($directory, $chunkname, [ @@ -68,4 +80,57 @@ public function storeChunk(StorageConfig $config, Range $range, UploadedFile $fi return Storage::disk($config->getDisk())->files($directory); } + + /** + * List all chunks from a directory with the given name. + * + * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param string $uuid + * + * @return array + */ + public function chunks(StorageConfig $config, string $uuid): array + { + $directory = $config->getChunkDirectory() . '/' . $uuid; + return Storage::disk($config->getDisk())->files($directory); + } + + /** + * Create a chunkname which contains range details. + * + * @param \LaraCrafts\ChunkUploader\Range\Range $range + * + * @return string + */ + public function buildChunkname(Range $range): string + { + $len = strlen($range->getTotal()); + return implode('-', [ + str_pad($range->getStart(), $len, '0', STR_PAD_LEFT), + str_pad($range->getEnd(), $len, '0', STR_PAD_LEFT), + ]); + } + + /** + * Check if a chunk exists. + * + * When chunkname is given it checks the exact chunk. Otherwise only the folder has to exists. + * + * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param string $uuid + * @param string|null $chunkname + * + * @return bool + */ + public function chunkExists(StorageConfig $config, string $uuid, string $chunkname = null): bool + { + $directory = $config->getChunkDirectory() . '/' . $uuid; + $disk = Storage::disk($config->getDisk()); + + if (! $disk->exists($directory)) { + return false; + } + + return $chunkname === null || $disk->exists($directory . '/' . $chunkname); + } } diff --git a/src/Range/RequestBodyRange.php b/src/Range/RequestBodyRange.php index 48b1f09..5380366 100644 --- a/src/Range/RequestBodyRange.php +++ b/src/Range/RequestBodyRange.php @@ -23,14 +23,15 @@ class RequestBodyRange implements Range * @param string $numberOfChunksKey The total number of the chunks * @param string $chunkSizeKey The size of the chunk * @param string $totalSizeKey The size of the original file + * @param int $indexBase The base (offset) of the index */ - public function __construct($request, string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey) + public function __construct($request, string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey, int $indexBase = 0) { if ($request instanceof Request) { $request = $request->request; } - $this->index = (int) $request->get($indexKey); + $this->index = (int) ($request->get($indexKey) - $indexBase); $this->numberOfChunks = (int) $request->get($numberOfChunksKey); $this->chunkSize = (int) $request->get($chunkSizeKey); // Must be double (which is an alias for float) for 32 bit systems @@ -40,10 +41,16 @@ public function __construct($request, string $indexKey, string $numberOfChunksKe throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $numberOfChunksKey)); } if ($this->index < 0) { - throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to zero', $indexKey)); + if ($indexBase === 0) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to zero', $indexKey)); + } + throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to %d', $indexKey, $indexBase)); } if ($this->index >= $this->numberOfChunks) { - throw new InvalidArgumentException(sprintf('`%s` must be smaller than `%s`', $indexKey, $numberOfChunksKey)); + if ($indexBase === 0) { + throw new InvalidArgumentException(sprintf('`%s` must be smaller than `%s`', $indexKey, $numberOfChunksKey)); + } + throw new InvalidArgumentException(sprintf('`%s` must be smaller than (`%s` + %d)', $indexKey, $numberOfChunksKey, $indexBase)); } if ($this->chunkSize < 1) { throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $chunkSizeKey)); diff --git a/src/UploadManager.php b/src/UploadManager.php index 2272942..f53fac9 100644 --- a/src/UploadManager.php +++ b/src/UploadManager.php @@ -6,6 +6,7 @@ use LaraCrafts\ChunkUploader\Driver\BlueimpUploadDriver; use LaraCrafts\ChunkUploader\Driver\DropzoneUploadDriver; use LaraCrafts\ChunkUploader\Driver\MonolithUploadDriver; +use LaraCrafts\ChunkUploader\Driver\ResumableJsUploadDriver; class UploadManager extends Manager { @@ -27,6 +28,11 @@ public function createDropzoneDriver() return new DropzoneUploadDriver($this->app['config']['chunk-uploader.dropzone']); } + public function createResumableJsDriver() + { + return new ResumableJsUploadDriver($this->app['config']['chunk-uploader.resumable-js']); + } + /** * Get the default driver name. * diff --git a/tests/Driver/ResumableJsUploadDriverTest.php b/tests/Driver/ResumableJsUploadDriverTest.php new file mode 100644 index 0000000..3a07543 --- /dev/null +++ b/tests/Driver/ResumableJsUploadDriverTest.php @@ -0,0 +1,270 @@ +app->make('config')->set('chunk-uploader.uploader', 'resumable-js'); + $this->app->make('config')->set('chunk-uploader.sweep', false); + $this->handler = $this->app->make(UploadHandler::class); + + Storage::fake('local'); + Event::fake(); + } + + public function testDriverInstance() + { + $manager = $this->app->make('chunk-uploader.upload-manager'); + + $this->assertInstanceOf(ResumableJsUploadDriver::class, $manager->driver()); + } + + public function testResumeWhenChunkDoesNotExists() + { + $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + + $request = Request::create('', Request::METHOD_GET, [ + 'resumableChunkNumber' => 2, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ]); + + $this->expectException(NotFoundHttpException::class); + + $this->createTestResponse($this->handler->handle($request)); + } + + public function testResume() + { + $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + + $request = Request::create('', Request::METHOD_GET, [ + 'resumableChunkNumber' => 1, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ]); + + $response = $this->createTestResponse($this->handler->handle($request)); + $response->assertSuccessful(); + } + + public function testUploadWhenFileParameterIsEmpty() + { + $request = Request::create('', Request::METHOD_POST); + + $this->expectException(BadRequestHttpException::class); + + $this->handler->handle($request); + } + + public function testUploadWhenFileParameterIsInvalid() + { + $file = Mockery::mock(UploadedFile::class) + ->makePartial(); + $file->shouldReceive('isValid') + ->andReturn(false); + + $request = Request::create('', Request::METHOD_POST, [], [], [ + 'file' => $file, + ]); + + $this->expectException(InternalServerErrorHttpException::class); + + $this->handler->handle($request); + } + + public function excludedPostParameterProvider() + { + return [ + 'resumableChunkNumber' => ['resumableChunkNumber'], + 'resumableTotalChunks' => ['resumableTotalChunks'], + 'resumableChunkSize' => ['resumableChunkSize'], + 'resumableTotalSize' => ['resumableTotalSize'], + 'resumableIdentifier' => ['resumableIdentifier'], + 'resumableFilename' => ['resumableFilename'], + 'resumableRelativePath' => ['resumableRelativePath'], + 'resumableCurrentChunkSize' => ['resumableCurrentChunkSize'], + 'resumableType' => ['resumableType'], + ]; + } + + /** + * @dataProvider excludedPostParameterProvider + */ + public function testPostParameterValidation($exclude) + { + $arr = [ + 'resumableChunkNumber' => 1, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ]; + + unset($arr[$exclude]); + + $request = Request::create('', Request::METHOD_POST, $arr, [], [ + 'file' => UploadedFile::fake() + ->create('test.txt', 100), + ]); + + $this->expectException(ValidationException::class); + + $this->handler->handle($request); + } + + public function testUploadFirstChunk() + { + $request = Request::create('', Request::METHOD_POST, [ + 'resumableChunkNumber' => 1, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ], [], [ + 'file' => UploadedFile::fake()->create('test.txt', 100), + ]); + + /** @var \Illuminate\Foundation\Testing\TestResponse $response */ + $response = $this->createTestResponse($this->handler->handle($request)); + $response->assertSuccessful(); + $response->assertJson(['done' => 50]); + + Storage::disk('local')->assertExists('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt/000-099'); + + Event::assertNotDispatched(FileUploaded::class, function ($event) { + return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + }); + } + + public function testUploadFirstChunkWithCallback() + { + $request = Request::create('', Request::METHOD_POST, [ + 'resumableChunkNumber' => 1, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ], [], [ + 'file' => UploadedFile::fake()->create('test.txt', 100), + ]); + + $callback = $this->createClosureMock($this->never()); + + $this->handler->handle($request, $callback); + + Event::assertNotDispatched(FileUploaded::class, function ($event) { + return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + }); + } + + public function testUploadLastChunk() + { + $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + + $request = Request::create('', Request::METHOD_POST, [ + 'resumableChunkNumber' => 2, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ], [], [ + 'file' => UploadedFile::fake() + ->create('test.txt', 100), + ]); + + /** @var \Illuminate\Foundation\Testing\TestResponse $response */ + $response = $this->createTestResponse($this->handler->handle($request)); + $response->assertSuccessful(); + $response->assertJson(['done' => 100]); + + Storage::disk('local')->assertExists('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt/100-199'); + Storage::disk('local')->assertExists('merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'); + + Event::assertDispatched(FileUploaded::class, function ($event) { + return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + }); + } + + public function testUploadLastChunkWithCallback() + { + $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + + $request = Request::create('', Request::METHOD_POST, [ + 'resumableChunkNumber' => 2, + 'resumableTotalChunks' => 2, + 'resumableChunkSize' => 100, + 'resumableTotalSize' => 200, + 'resumableIdentifier' => '200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', + 'resumableFilename' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableRelativePath' => '0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zf.txt', + 'resumableCurrentChunkSize' => 100, + 'resumableType' => 'text/plain', + ], [], [ + 'file' => UploadedFile::fake()->create('test.txt', 100), + ]); + + $callback = $this->createClosureMock( + $this->once(), + 'local', + 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt' + ); + + $this->handler->handle($request, $callback); + + Event::assertDispatched(FileUploaded::class, function ($event) { + return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + }); + } +} From 67a78f5eb5ec89edc5e068afbe0046ae5fe4988e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Fri, 6 Mar 2020 23:59:24 +0100 Subject: [PATCH 02/11] Test not allowed methods --- tests/Driver/ResumableJsUploadDriverTest.php | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Driver/ResumableJsUploadDriverTest.php b/tests/Driver/ResumableJsUploadDriverTest.php index 3a07543..4b0c7e1 100644 --- a/tests/Driver/ResumableJsUploadDriverTest.php +++ b/tests/Driver/ResumableJsUploadDriverTest.php @@ -14,6 +14,7 @@ use LaraCrafts\ChunkUploader\UploadHandler; use Mockery; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class ResumableJsUploadDriverTest extends TestCase @@ -42,6 +43,33 @@ public function testDriverInstance() $this->assertInstanceOf(ResumableJsUploadDriver::class, $manager->driver()); } + public function notAllowedRequestMethods() + { + return [ + 'HEAD' => [Request::METHOD_HEAD], + 'PUT' => [Request::METHOD_PUT], + 'PATCH' => [Request::METHOD_PATCH], + 'DELETE' => [Request::METHOD_DELETE], + 'PURGE' => [Request::METHOD_PURGE], + 'OPTIONS' => [Request::METHOD_OPTIONS], + 'TRACE' => [Request::METHOD_TRACE], + 'CONNECT' => [Request::METHOD_CONNECT], + ]; + } + + /** + * @dataProvider notAllowedRequestMethods + */ + + public function testMethodNotAllowed($requestMethod) + { + $request = Request::create('', $requestMethod); + + $this->expectException(MethodNotAllowedHttpException::class); + + $this->createTestResponse($this->handler->handle($request)); + } + public function testResumeWhenChunkDoesNotExists() { $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); From a33231d1c108efd12890baf0557d6c1957caa390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Sat, 7 Mar 2020 14:02:40 +0100 Subject: [PATCH 03/11] Separate logic of request body range depending on base --- src/Driver/DropzoneUploadDriver.php | 4 +- src/Driver/ResumableJsUploadDriver.php | 12 +- src/Range/OneBasedRequestBodyRange.php | 92 +++++++++++ src/Range/RequestBodyRange.php | 97 ++++++------ src/Range/ZeroBasedRequestBodyRange.php | 82 ++++++++++ tests/Range/OneBasedRequestBodyRangeTest.php | 145 ++++++++++++++++++ ....php => ZeroBasedRequestBodyRangeTest.php} | 20 +-- 7 files changed, 379 insertions(+), 73 deletions(-) create mode 100644 src/Range/OneBasedRequestBodyRange.php create mode 100644 src/Range/ZeroBasedRequestBodyRange.php create mode 100644 tests/Range/OneBasedRequestBodyRangeTest.php rename tests/Range/{RequestBodyRangeTest.php => ZeroBasedRequestBodyRangeTest.php} (88%) diff --git a/src/Driver/DropzoneUploadDriver.php b/src/Driver/DropzoneUploadDriver.php index 63202ed..3384826 100644 --- a/src/Driver/DropzoneUploadDriver.php +++ b/src/Driver/DropzoneUploadDriver.php @@ -7,7 +7,7 @@ use Illuminate\Http\UploadedFile; use InvalidArgumentException; use LaraCrafts\ChunkUploader\Helper\ChunkHelpers; -use LaraCrafts\ChunkUploader\Range\RequestBodyRange; +use LaraCrafts\ChunkUploader\Range\ZeroBasedRequestBodyRange; use LaraCrafts\ChunkUploader\Response\PercentageJsonResponse; use LaraCrafts\ChunkUploader\StorageConfig; use Symfony\Component\HttpFoundation\Response; @@ -123,7 +123,7 @@ private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { try { - $range = new RequestBodyRange( + $range = new ZeroBasedRequestBodyRange( $request, 'dzchunkindex', 'dztotalchunkcount', diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php index d66722e..a90f3e2 100644 --- a/src/Driver/ResumableJsUploadDriver.php +++ b/src/Driver/ResumableJsUploadDriver.php @@ -8,7 +8,7 @@ use Illuminate\Http\UploadedFile; use InvalidArgumentException; use LaraCrafts\ChunkUploader\Helper\ChunkHelpers; -use LaraCrafts\ChunkUploader\Range\RequestBodyRange; +use LaraCrafts\ChunkUploader\Range\OneBasedRequestBodyRange; use LaraCrafts\ChunkUploader\Response\PercentageJsonResponse; use LaraCrafts\ChunkUploader\StorageConfig; use Symfony\Component\HttpFoundation\Response; @@ -86,13 +86,12 @@ public function resume(Request $request, StorageConfig $config): Response $this->validateChunkRequest($request); try { - $range = new RequestBodyRange( + $range = new OneBasedRequestBodyRange( $request->query, $this->buildParameterName('chunk-number'), $this->buildParameterName('total-chunks'), $this->buildParameterName('chunk-size'), - $this->buildParameterName('total-size'), - 1 + $this->buildParameterName('total-size') ); } catch (InvalidArgumentException $e) { throw new BadRequestHttpException($e->getMessage(), $e); @@ -151,13 +150,12 @@ private function validateChunkRequest(Request $request) private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { try { - $range = new RequestBodyRange( + $range = new OneBasedRequestBodyRange( $request, $this->buildParameterName('chunk-number'), $this->buildParameterName('total-chunks'), $this->buildParameterName('chunk-size'), - $this->buildParameterName('total-size'), - 1 + $this->buildParameterName('total-size') ); } catch (InvalidArgumentException $e) { throw new BadRequestHttpException($e->getMessage(), $e); diff --git a/src/Range/OneBasedRequestBodyRange.php b/src/Range/OneBasedRequestBodyRange.php new file mode 100644 index 0000000..a5eedec --- /dev/null +++ b/src/Range/OneBasedRequestBodyRange.php @@ -0,0 +1,92 @@ +numberOfChunks <= 0) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $numberOfChunksKey)); + } + } + + /** + * @param string $indexKey + * @param string $numberOfChunksKey + */ + protected function validateIndexKey(string $indexKey, string $numberOfChunksKey): void + { + if ($this->index < 1) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $indexKey)); + } + if ($this->index > $this->numberOfChunks) { + throw new InvalidArgumentException(sprintf('`%s` must be smaller than or equal to `%s`', $indexKey, $numberOfChunksKey)); + } + } + + /** + * @param string $indexKey + * @param string $numberOfChunksKey + * @param string $chunkSizeKey + * @param string $totalSizeKey + */ + protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void { + if ($this->totalSize < 1) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $totalSizeKey)); + } elseif ($this->totalSize <= ($this->index - 1) * $this->chunkSize) { + throw new InvalidArgumentException( + sprintf('`%s` must be greater than or equal to the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $indexKey) + ); + } elseif ($this->totalSize > $this->numberOfChunks * $this->chunkSize) { + throw new InvalidArgumentException( + sprintf('`%s` must be smaller than or equal to the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $numberOfChunksKey) + ); + } + } + + /** + * {@inheritDoc} + */ + public function getStart(): float + { + return ($this->index - 1) * $this->chunkSize; + } + + /** + * {@inheritDoc} + */ + public function getEnd(): float + { + $end = ($this->index * $this->chunkSize) - 1; + + $sizeIndex = $this->totalSize - 1; + if ($end > ($sizeIndex)) { + return $sizeIndex; + } + + return $end; + } + + /** + * {@inheritDoc} + */ + public function isFirst(): bool + { + return $this->index === 1; + } + + /** + * {@inheritDoc} + */ + public function isLast(): bool + { + return $this->index === $this->numberOfChunks; + } +} diff --git a/src/Range/RequestBodyRange.php b/src/Range/RequestBodyRange.php index 5380366..135c35c 100644 --- a/src/Range/RequestBodyRange.php +++ b/src/Range/RequestBodyRange.php @@ -5,15 +5,15 @@ use Illuminate\Http\Request; use InvalidArgumentException; -class RequestBodyRange implements Range +abstract class RequestBodyRange implements Range { - private $index; + protected $index; - private $numberOfChunks; + protected $numberOfChunks; - private $totalSize; + protected $totalSize; - private $chunkSize; + protected $chunkSize; /** * RequestRange constructor. @@ -23,73 +23,68 @@ class RequestBodyRange implements Range * @param string $numberOfChunksKey The total number of the chunks * @param string $chunkSizeKey The size of the chunk * @param string $totalSizeKey The size of the original file - * @param int $indexBase The base (offset) of the index */ - public function __construct($request, string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey, int $indexBase = 0) + public function __construct($request, string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey) { if ($request instanceof Request) { $request = $request->request; } - $this->index = (int) ($request->get($indexKey) - $indexBase); + $this->index = (int) $request->get($indexKey); $this->numberOfChunks = (int) $request->get($numberOfChunksKey); $this->chunkSize = (int) $request->get($chunkSizeKey); // Must be double (which is an alias for float) for 32 bit systems $this->totalSize = (double) $request->get($totalSizeKey); + $this->validateNumberOfChunks($numberOfChunksKey); + $this->validateIndexKey($indexKey, $numberOfChunksKey); + $this->validateChunkSize($chunkSizeKey); + $this->validateTotalSize($indexKey, $numberOfChunksKey, $chunkSizeKey, $totalSizeKey); + } + + /** + * @param string $numberOfChunksKey + */ + protected function validateNumberOfChunks(string $numberOfChunksKey): void + { if ($this->numberOfChunks <= 0) { throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $numberOfChunksKey)); } - if ($this->index < 0) { - if ($indexBase === 0) { - throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to zero', $indexKey)); - } - throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to %d', $indexKey, $indexBase)); - } - if ($this->index >= $this->numberOfChunks) { - if ($indexBase === 0) { - throw new InvalidArgumentException(sprintf('`%s` must be smaller than `%s`', $indexKey, $numberOfChunksKey)); - } - throw new InvalidArgumentException(sprintf('`%s` must be smaller than (`%s` + %d)', $indexKey, $numberOfChunksKey, $indexBase)); - } + } + + /** + * @param string $indexKey + * @param string $numberOfChunksKey + */ + abstract protected function validateIndexKey(string $indexKey, string $numberOfChunksKey): void; + + /** + * @param string $chunkSizeKey + */ + protected function validateChunkSize(string $chunkSizeKey): void + { if ($this->chunkSize < 1) { throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $chunkSizeKey)); } - if ($this->totalSize < 1) { - throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $totalSizeKey)); - } elseif ($this->totalSize <= $this->index * $this->chunkSize) { - throw new InvalidArgumentException( - sprintf('`%s` must be greater than the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $indexKey) - ); - } elseif ($this->totalSize > $this->numberOfChunks * $this->chunkSize) { - throw new InvalidArgumentException( - sprintf('`%s` must be smaller than or equal to the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $numberOfChunksKey) - ); - } } /** - * {@inheritDoc} + * @param string $indexKey + * @param string $numberOfChunksKey + * @param string $chunkSizeKey + * @param string $totalSizeKey */ - public function getStart(): float - { - return $this->index * $this->chunkSize; - } + abstract protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void; /** * {@inheritDoc} */ - public function getEnd(): float - { - $end = (($this->index + 1) * $this->chunkSize) - 1; - - $sizeIndex = $this->totalSize - 1; - if ($end > ($sizeIndex)) { - return $sizeIndex; - } + abstract public function getStart(): float; - return $end; - } + /** + * {@inheritDoc} + */ + abstract public function getEnd(): float; /** * {@inheritDoc} @@ -102,18 +97,12 @@ public function getTotal(): float /** * {@inheritDoc} */ - public function isFirst(): bool - { - return $this->index === 0; - } + abstract public function isFirst(): bool; /** * {@inheritDoc} */ - public function isLast(): bool - { - return $this->index === $this->numberOfChunks - 1; - } + abstract public function isLast(): bool; /** * @param $uploadedChunks diff --git a/src/Range/ZeroBasedRequestBodyRange.php b/src/Range/ZeroBasedRequestBodyRange.php new file mode 100644 index 0000000..d1d5f48 --- /dev/null +++ b/src/Range/ZeroBasedRequestBodyRange.php @@ -0,0 +1,82 @@ +index < 0) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than or equal to zero', $indexKey)); + } + if ($this->index >= $this->numberOfChunks) { + throw new InvalidArgumentException(sprintf('`%s` must be smaller than `%s`', $indexKey, $numberOfChunksKey)); + } + } + + /** + * @param string $indexKey + * @param string $numberOfChunksKey + * @param string $chunkSizeKey + * @param string $totalSizeKey + */ + protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void { + if ($this->totalSize < 1) { + throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $totalSizeKey)); + } elseif ($this->totalSize <= $this->index * $this->chunkSize) { + throw new InvalidArgumentException( + sprintf('`%s` must be greater than the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $indexKey) + ); + } elseif ($this->totalSize > $this->numberOfChunks * $this->chunkSize) { + throw new InvalidArgumentException( + sprintf('`%s` must be smaller than or equal to the multiple of `%s` and `%s`', $totalSizeKey, $chunkSizeKey, $numberOfChunksKey) + ); + } + } + + /** + * {@inheritDoc} + */ + public function getStart(): float + { + return $this->index * $this->chunkSize; + } + + /** + * {@inheritDoc} + */ + public function getEnd(): float + { + $end = (($this->index + 1) * $this->chunkSize) - 1; + + $sizeIndex = $this->totalSize - 1; + if ($end > ($sizeIndex)) { + return $sizeIndex; + } + + return $end; + } + + /** + * {@inheritDoc} + */ + public function isFirst(): bool + { + return $this->index === 0; + } + + /** + * {@inheritDoc} + */ + public function isLast(): bool + { + return $this->index === ($this->numberOfChunks - 1); + } +} diff --git a/tests/Range/OneBasedRequestBodyRangeTest.php b/tests/Range/OneBasedRequestBodyRangeTest.php new file mode 100644 index 0000000..8cf3f0a --- /dev/null +++ b/tests/Range/OneBasedRequestBodyRangeTest.php @@ -0,0 +1,145 @@ + [5, 0, 20, 190, '`numberOfChunks` must be greater than zero'], + 'Number of chunks size smaller than zero' => [5, -1, 20, 190, '`numberOfChunks` must be greater than zero'], + 'Index smaller than zero' => [0, 10, 20, 190, '`index` must be greater than'], + 'Index greater than the number of chunks' => [15, 10, 20, 190, '`index` must be smaller than or equal to `numberOfChunks`'], + 'Chunk size equal to zero' => [5, 10, 0, 190, '`chunkSize` must be greater than zero'], + 'Chunk size smaller than zero' => [5, 10, -1, 190, '`chunkSize` must be greater than zero'], + 'Total size equal to zero' => [5, 10, 20, 0, '`totalSize` must be greater than zero'], + 'Total size smaller than zero' => [5, 10, 20, -1, '`totalSize` must be greater than zero'], + 'Total size too small' => [5, 10, 20, 80, '`totalSize` must be greater than or equal to the multiple of `chunkSize` and `index`'], + 'Total size too big' => [5, 10, 20, 201, '`totalSize` must be smaller than or equal to the multiple of `chunkSize` and `numberOfChunks`'], + ]; + } + + /** + * @dataProvider invalidArgumentProvider + * + * @param $index + * @param $numberOfChunks + * @param $chunkSize + * @param $totalSize + * @param $expectedExceptionMessage + */ + public function testArgumentValidation($index, $numberOfChunks, $chunkSize, $totalSize, $expectedExceptionMessage) + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + + $this->createRequestBodyRange($index, $numberOfChunks, $chunkSize, $totalSize); + } + + public function testIsFirst() + { + $range = $this->createRequestBodyRange(1, 2, 1, 2); + $this->assertTrue($range->isFirst()); + + $range = $this->createRequestBodyRange(2, 2, 1, 2); + $this->assertFalse($range->isFirst()); + } + + public function testIsLast() + { + $range = $this->createRequestBodyRange(2, 2, 1, 2); + $this->assertTrue($range->isLast()); + + $range = $this->createRequestBodyRange(1, 2, 1, 2); + $this->assertFalse($range->isLast()); + } + + public function testIsFirstAndIsLast() + { + $range = $this->createRequestBodyRange(1, 1, 1, 1); + $this->assertTrue($range->isLast()); + $this->assertTrue($range->isLast()); + } + + public function testGetTotal() + { + $range = $this->createRequestBodyRange(4, 10, 20, 190); + $this->assertEquals(190, $range->getTotal()); + } + + public function testGetStart() + { + $range = $this->createRequestBodyRange(5, 10, 20, 190); + $this->assertEquals(80, $range->getStart()); + } + + public function testGetEnd() + { + $range = $this->createRequestBodyRange(5, 10, 20, 190); + $this->assertEquals(99, $range->getEnd()); + + $range = $this->createRequestBodyRange(10, 10, 20, 190); + $this->assertEquals(189, $range->getEnd()); + } + + public function testGetPercentage() + { + $range = $this->createRequestBodyRange(4, 10, 20, 190); + $this->assertEquals(100, $range->getPercentage(range(0, 9))); + + $range = $this->createRequestBodyRange(4, 10, 20, 190); + $this->assertEquals(90, $range->getPercentage(range(0, 8))); + } + + public function testIsFinished() + { + $range = $this->createRequestBodyRange(4, 10, 20, 190); + $this->assertTrue($range->isFinished(range(0, 9))); + + $range = $this->createRequestBodyRange(4, 10, 20, 190); + $this->assertFalse($range->isFinished(range(0, 8))); + } + + public function testCreateFromRequest() + { + $request = new Request([], [ + 'index' => 5, + 'numberOfChunks' => 10, + 'chunkSize' => 20, + 'totalSize' => 190, + ]); + + $range = new OneBasedRequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); + + $this->assertEquals(80, $range->getStart()); + $this->assertEquals(99, $range->getEnd()); + $this->assertEquals(190, $range->getTotal()); + } + + /** + * @param int $index + * @param int $numberOfChunks + * @param int $chunkSize + * @param float $totalSize + * + * @return \LaraCrafts\ChunkUploader\Range\OneBasedRequestBodyRange + */ + private function createRequestBodyRange(int $index, int $numberOfChunks, int $chunkSize, float $totalSize) + { + $request = new ParameterBag([ + 'index' => (string) $index, + 'numberOfChunks' => (string) $numberOfChunks, + 'chunkSize' => (string) $chunkSize, + 'totalSize' => (string) $totalSize, + ]); + + return new OneBasedRequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); + } +} diff --git a/tests/Range/RequestBodyRangeTest.php b/tests/Range/ZeroBasedRequestBodyRangeTest.php similarity index 88% rename from tests/Range/RequestBodyRangeTest.php rename to tests/Range/ZeroBasedRequestBodyRangeTest.php index 19e0d1d..bce10dc 100644 --- a/tests/Range/RequestBodyRangeTest.php +++ b/tests/Range/ZeroBasedRequestBodyRangeTest.php @@ -4,11 +4,11 @@ use Illuminate\Http\Request; use InvalidArgumentException; -use LaraCrafts\ChunkUploader\Range\RequestBodyRange; +use LaraCrafts\ChunkUploader\Range\ZeroBasedRequestBodyRange; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\ParameterBag; -class RequestBodyRangeTest extends TestCase +class ZeroBasedRequestBodyRangeTest extends TestCase { public function invalidArgumentProvider() { @@ -117,7 +117,7 @@ public function testCreateFromRequest() 'totalSize' => 190, ]); - $range = new RequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); + $range = new ZeroBasedRequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); $this->assertEquals(80, $range->getStart()); $this->assertEquals(99, $range->getEnd()); @@ -125,14 +125,14 @@ public function testCreateFromRequest() } /** - * @param $index - * @param $numberOfChunks - * @param $chunkSize - * @param $totalSize + * @param int $index + * @param int $numberOfChunks + * @param int $chunkSize + * @param float $totalSize * - * @return \LaraCrafts\ChunkUploader\Range\RequestBodyRange + * @return \LaraCrafts\ChunkUploader\Range\ZeroBasedRequestBodyRange */ - private function createRequestBodyRange($index, $numberOfChunks, $chunkSize, $totalSize) + private function createRequestBodyRange(int $index, int $numberOfChunks, int $chunkSize, float $totalSize) { $request = new ParameterBag([ 'index' => (string) $index, @@ -141,6 +141,6 @@ private function createRequestBodyRange($index, $numberOfChunks, $chunkSize, $to 'totalSize' => (string) $totalSize, ]); - return new RequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); + return new ZeroBasedRequestBodyRange($request, 'index', 'numberOfChunks', 'chunkSize', 'totalSize'); } } From 77707520317191aa21896c944697b83dcad9212a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Sat, 7 Mar 2020 14:06:18 +0100 Subject: [PATCH 04/11] Fix style --- src/Range/OneBasedRequestBodyRange.php | 4 ++-- src/Range/ZeroBasedRequestBodyRange.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Range/OneBasedRequestBodyRange.php b/src/Range/OneBasedRequestBodyRange.php index a5eedec..f14c74f 100644 --- a/src/Range/OneBasedRequestBodyRange.php +++ b/src/Range/OneBasedRequestBodyRange.php @@ -2,7 +2,6 @@ namespace LaraCrafts\ChunkUploader\Range; -use Illuminate\Http\Request; use InvalidArgumentException; class OneBasedRequestBodyRange extends RequestBodyRange @@ -37,7 +36,8 @@ protected function validateIndexKey(string $indexKey, string $numberOfChunksKey) * @param string $chunkSizeKey * @param string $totalSizeKey */ - protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void { + protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void + { if ($this->totalSize < 1) { throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $totalSizeKey)); } elseif ($this->totalSize <= ($this->index - 1) * $this->chunkSize) { diff --git a/src/Range/ZeroBasedRequestBodyRange.php b/src/Range/ZeroBasedRequestBodyRange.php index d1d5f48..1752c4e 100644 --- a/src/Range/ZeroBasedRequestBodyRange.php +++ b/src/Range/ZeroBasedRequestBodyRange.php @@ -2,7 +2,6 @@ namespace LaraCrafts\ChunkUploader\Range; -use Illuminate\Http\Request; use InvalidArgumentException; class ZeroBasedRequestBodyRange extends RequestBodyRange @@ -27,7 +26,8 @@ protected function validateIndexKey(string $indexKey, string $numberOfChunksKey) * @param string $chunkSizeKey * @param string $totalSizeKey */ - protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void { + protected function validateTotalSize(string $indexKey, string $numberOfChunksKey, string $chunkSizeKey, string $totalSizeKey): void + { if ($this->totalSize < 1) { throw new InvalidArgumentException(sprintf('`%s` must be greater than zero', $totalSizeKey)); } elseif ($this->totalSize <= $this->index * $this->chunkSize) { From ccf9e163d5c8f41666e598064e68fc49f5f86710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Tue, 24 Mar 2020 12:01:36 +0100 Subject: [PATCH 05/11] Fix code format in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2707588..84f0a73 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,8 @@ class MyController extends Controller public function myFunction(Request $request) { $handler = app()->make(UploadHandler::class); - return $handler->handle($request);} + return $handler->handle($request); + } } ``` From 564e159a880925757b497c939f3828b42c1f5af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Wed, 25 Mar 2020 10:05:36 +0100 Subject: [PATCH 06/11] Update config/chunk-uploader.php Co-Authored-By: Choraimy Kroonstuiver --- config/chunk-uploader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/chunk-uploader.php b/config/chunk-uploader.php index 650dcbd..2a86322 100644 --- a/config/chunk-uploader.php +++ b/config/chunk-uploader.php @@ -135,7 +135,7 @@ // HTTP method for chunk test request. 'test-method' => \Illuminate\Http\Request::METHOD_GET, // HTTP method to use when sending chunks to the server (POST, PUT, PATCH). - 'upload-method' => \Illuminate\Http\Request::METHOD_POST, + 'upload-method' => Illuminate\Http\Request::METHOD_POST, // Extra prefix added before the name of each parameter included in the multipart POST or in the test GET. 'parameter-namespace' => '', From f65fac410622800dd131796d455f26c74f96e3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Wed, 25 Mar 2020 10:05:46 +0100 Subject: [PATCH 07/11] Update config/chunk-uploader.php Co-Authored-By: Choraimy Kroonstuiver --- config/chunk-uploader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/chunk-uploader.php b/config/chunk-uploader.php index 2a86322..04c98f5 100644 --- a/config/chunk-uploader.php +++ b/config/chunk-uploader.php @@ -133,7 +133,7 @@ 'param' => 'file', // HTTP method for chunk test request. - 'test-method' => \Illuminate\Http\Request::METHOD_GET, + 'test-method' => Illuminate\Http\Request::METHOD_GET, // HTTP method to use when sending chunks to the server (POST, PUT, PATCH). 'upload-method' => Illuminate\Http\Request::METHOD_POST, From 48cbe00a4e547f50a5bc530c782e0376d9a70869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Wed, 25 Mar 2020 10:06:57 +0100 Subject: [PATCH 08/11] Update src/Driver/ResumableJsUploadDriver.php Co-Authored-By: Choraimy Kroonstuiver --- src/Driver/ResumableJsUploadDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php index a90f3e2..9248d48 100644 --- a/src/Driver/ResumableJsUploadDriver.php +++ b/src/Driver/ResumableJsUploadDriver.php @@ -187,7 +187,7 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ return new PercentageJsonResponse(100); } - private function buildParameterName($key) + private function buildParameterName($key): string { if (! array_key_exists($key, $this->parameterNames)) { throw new InvalidArgumentException(sprintf('`%s` is an invalid key for parameter name', $key)); From 752216a49d00aea24ac9efb96fd6060cecb8701b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Wed, 25 Mar 2020 10:35:12 +0100 Subject: [PATCH 09/11] Use hash name for target filename --- src/Driver/BlueimpUploadDriver.php | 15 +++++--- src/Driver/DropzoneUploadDriver.php | 15 +++----- src/Driver/ResumableJsUploadDriver.php | 13 ++----- src/Identifier/Identifier.php | 9 +---- tests/Driver/BlueimpUploadDriverTest.php | 32 +++++++++------- tests/Driver/DropzoneUploadDriverTest.php | 40 +++++++++++--------- tests/Driver/ResumableJsUploadDriverTest.php | 33 ++++++++-------- tests/Identifier/SessionIdentifierTest.php | 2 +- 8 files changed, 79 insertions(+), 80 deletions(-) diff --git a/src/Driver/BlueimpUploadDriver.php b/src/Driver/BlueimpUploadDriver.php index 5725248..d4d056e 100644 --- a/src/Driver/BlueimpUploadDriver.php +++ b/src/Driver/BlueimpUploadDriver.php @@ -96,7 +96,7 @@ public function download(Request $request, StorageConfig $config) $request->validate([$this->fileParam => 'required']); $filename = $request->query($this->fileParam); - if (! $this->chunkExists($config, $filename)) { + if (!$this->chunkExists($config, $filename)) { return new JsonResponse([ 'file' => null, ]); @@ -136,18 +136,20 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa throw new BadRequestHttpException($e->getMessage(), $e); } - $filename = $this->identifier->generateUploadedFileIdentifierName($file); + $uuid = $this->identifier->generateUploadedFileIdentifierName($file); - $chunks = $this->storeChunk($config, $range, $file, $filename); + $chunks = $this->storeChunk($config, $range, $file, $uuid); - if (! $range->isLast()) { + if (!$range->isLast()) { return new PercentageJsonResponse($range->getPercentage()); } - $path = $this->mergeChunks($config, $chunks, $filename); + $targetFilename = $file->hashName(); + + $path = $this->mergeChunks($config, $chunks, $targetFilename); if ($config->sweep()) { - $this->deleteChunkDirectory($config, $filename); + $this->deleteChunkDirectory($config, $uuid); } $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); @@ -158,6 +160,7 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa /** * @param Request $request * @param StorageConfig $config + * * @return Response */ public function delete(Request $request, StorageConfig $config) diff --git a/src/Driver/DropzoneUploadDriver.php b/src/Driver/DropzoneUploadDriver.php index 3384826..a13282d 100644 --- a/src/Driver/DropzoneUploadDriver.php +++ b/src/Driver/DropzoneUploadDriver.php @@ -134,23 +134,20 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ throw new BadRequestHttpException($e->getMessage(), $e); } - $filename = $request->post('dzuuid'); + $uuid = $request->post('dzuuid'); - // On windows you can not create a file whose name ends with a dot - if ($file->getClientOriginalExtension()) { - $filename .= '.' . $file->getClientOriginalExtension(); - } - - $chunks = $this->storeChunk($config, $range, $file, $filename); + $chunks = $this->storeChunk($config, $range, $file, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $path = $this->mergeChunks($config, $chunks, $filename); + $targetFilename = $file->hashName(); + + $path = $this->mergeChunks($config, $chunks, $targetFilename); if ($config->sweep()) { - $this->deleteChunkDirectory($config, $filename); + $this->deleteChunkDirectory($config, $uuid); } $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php index 9248d48..8433f15 100644 --- a/src/Driver/ResumableJsUploadDriver.php +++ b/src/Driver/ResumableJsUploadDriver.php @@ -161,25 +161,20 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ throw new BadRequestHttpException($e->getMessage(), $e); } - $filename = $request->post($this->buildParameterName('identifier')); + $uuid = $request->post($this->buildParameterName('identifier')); - $chunks = $this->storeChunk($config, $range, $file, $filename); + $chunks = $this->storeChunk($config, $range, $file, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $targetFilename = $filename; - - // On windows you can not create a file whose name ends with a dot - if ($file->getClientOriginalExtension()) { - $targetFilename .= '.' . $file->getClientOriginalExtension(); - } + $targetFilename = $file->hashName(); $path = $this->mergeChunks($config, $chunks, $targetFilename); if ($config->sweep()) { - $this->deleteChunkDirectory($config, $filename); + $this->deleteChunkDirectory($config, $uuid); } $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); diff --git a/src/Identifier/Identifier.php b/src/Identifier/Identifier.php index 73bcbab..7cf063f 100644 --- a/src/Identifier/Identifier.php +++ b/src/Identifier/Identifier.php @@ -22,13 +22,6 @@ public function generateUploadedFileIdentifierName(UploadedFile $file): string { $data = $file->getClientOriginalName(); - $filename = $this->generateIdentifier($data); - - // On windows you can not create a file whose name ends with a dot - if ($file->getClientOriginalExtension()) { - $filename .= '.' . $file->getClientOriginalExtension(); - } - - return $filename; + return $this->generateIdentifier($data) . '.' . $file->guessExtension(); } } diff --git a/tests/Driver/BlueimpUploadDriverTest.php b/tests/Driver/BlueimpUploadDriverTest.php index 73a72be..8cba11e 100644 --- a/tests/Driver/BlueimpUploadDriverTest.php +++ b/tests/Driver/BlueimpUploadDriverTest.php @@ -137,8 +137,9 @@ public function testUploadWhenFileParameterIsInvalid() public function testUploadFirstChunk() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ], [ 'HTTP_CONTENT_RANGE' => 'bytes 0-99/200', ]); @@ -150,15 +151,16 @@ public function testUploadFirstChunk() Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt/000-099'); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } public function testUploadFirstChunkWithCallback() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ], [ 'HTTP_CONTENT_RANGE' => 'bytes 0-99/200', ]); @@ -167,8 +169,8 @@ public function testUploadFirstChunkWithCallback() $this->handler->handle($request, $callback); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } @@ -176,8 +178,9 @@ public function testUploadLastChunk() { $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt', '000'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ], [ 'HTTP_CONTENT_RANGE' => 'bytes 100-199/200', ]); @@ -188,10 +191,10 @@ public function testUploadLastChunk() $response->assertJson(['done' => 100]); Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt/100-199'); - Storage::disk('local')->assertExists('merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'); + Storage::disk('local')->assertExists($file->hashName('merged')); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } @@ -199,8 +202,9 @@ public function testUploadLastChunkWithCallback() { $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt', '000'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ], [ 'HTTP_CONTENT_RANGE' => 'bytes 100-199/200', ]); @@ -208,13 +212,13 @@ public function testUploadLastChunkWithCallback() $callback = $this->createClosureMock( $this->once(), 'local', - 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt' + $file->hashName('merged') ); $this->handler->handle($request, $callback); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } diff --git a/tests/Driver/DropzoneUploadDriverTest.php b/tests/Driver/DropzoneUploadDriverTest.php index bfd0a18..1b27320 100644 --- a/tests/Driver/DropzoneUploadDriverTest.php +++ b/tests/Driver/DropzoneUploadDriverTest.php @@ -143,6 +143,7 @@ public function testPostParameterValidation($exclude) public function testUploadFirstChunk() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'dzuuid' => '2494cefe4d234bd331aeb4514fe97d810efba29b', 'dzchunkindex' => 0, @@ -151,7 +152,7 @@ public function testUploadFirstChunk() 'dztotalchunkcount' => 2, 'dzchunkbyteoffset' => 100, ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); /** @var \Illuminate\Foundation\Testing\TestResponse $response */ @@ -159,15 +160,16 @@ public function testUploadFirstChunk() $response->assertSuccessful(); $response->assertJson(['done' => 50]); - Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt/000-099'); + Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b/000-099'); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } public function testUploadFirstChunkWithCallback() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'dzuuid' => '2494cefe4d234bd331aeb4514fe97d810efba29b', 'dzchunkindex' => 0, @@ -176,22 +178,23 @@ public function testUploadFirstChunkWithCallback() 'dztotalchunkcount' => 2, 'dzchunkbyteoffset' => 100, ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); $callback = $this->createClosureMock($this->never()); $this->handler->handle($request, $callback); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } public function testUploadLastChunk() { - $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt', '000'); + $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b', '000'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'dzuuid' => '2494cefe4d234bd331aeb4514fe97d810efba29b', 'dzchunkindex' => 1, @@ -200,7 +203,7 @@ public function testUploadLastChunk() 'dztotalchunkcount' => 2, 'dzchunkbyteoffset' => 100, ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); /** @var \Illuminate\Foundation\Testing\TestResponse $response */ @@ -208,18 +211,19 @@ public function testUploadLastChunk() $response->assertSuccessful(); $response->assertJson(['done' => 100]); - Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt/100-199'); - Storage::disk('local')->assertExists('merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'); + Storage::disk('local')->assertExists('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b/100-199'); + Storage::disk('local')->assertExists($file->hashName('merged')); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } public function testUploadLastChunkWithCallback() { - $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b.txt', '000'); + $this->createFakeLocalFile('chunks/2494cefe4d234bd331aeb4514fe97d810efba29b', '000'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'dzuuid' => '2494cefe4d234bd331aeb4514fe97d810efba29b', 'dzchunkindex' => 1, @@ -228,19 +232,19 @@ public function testUploadLastChunkWithCallback() 'dztotalchunkcount' => 2, 'dzchunkbyteoffset' => 100, ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); $callback = $this->createClosureMock( $this->once(), 'local', - 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt' + $file->hashName('merged') ); $this->handler->handle($request, $callback); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/2494cefe4d234bd331aeb4514fe97d810efba29b.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } } diff --git a/tests/Driver/ResumableJsUploadDriverTest.php b/tests/Driver/ResumableJsUploadDriverTest.php index 4b0c7e1..506111b 100644 --- a/tests/Driver/ResumableJsUploadDriverTest.php +++ b/tests/Driver/ResumableJsUploadDriverTest.php @@ -182,6 +182,7 @@ public function testPostParameterValidation($exclude) public function testUploadFirstChunk() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'resumableChunkNumber' => 1, 'resumableTotalChunks' => 2, @@ -193,7 +194,7 @@ public function testUploadFirstChunk() 'resumableCurrentChunkSize' => 100, 'resumableType' => 'text/plain', ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); /** @var \Illuminate\Foundation\Testing\TestResponse $response */ @@ -203,13 +204,14 @@ public function testUploadFirstChunk() Storage::disk('local')->assertExists('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt/000-099'); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } public function testUploadFirstChunkWithCallback() { + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'resumableChunkNumber' => 1, 'resumableTotalChunks' => 2, @@ -221,15 +223,15 @@ public function testUploadFirstChunkWithCallback() 'resumableCurrentChunkSize' => 100, 'resumableType' => 'text/plain', ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); $callback = $this->createClosureMock($this->never()); $this->handler->handle($request, $callback); - Event::assertNotDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + Event::assertNotDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } @@ -237,6 +239,7 @@ public function testUploadLastChunk() { $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'resumableChunkNumber' => 2, 'resumableTotalChunks' => 2, @@ -248,8 +251,7 @@ public function testUploadLastChunk() 'resumableCurrentChunkSize' => 100, 'resumableType' => 'text/plain', ], [], [ - 'file' => UploadedFile::fake() - ->create('test.txt', 100), + 'file' => $file, ]); /** @var \Illuminate\Foundation\Testing\TestResponse $response */ @@ -258,10 +260,10 @@ public function testUploadLastChunk() $response->assertJson(['done' => 100]); Storage::disk('local')->assertExists('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt/100-199'); - Storage::disk('local')->assertExists('merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'); + Storage::disk('local')->assertExists($file->hashName('merged')); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } @@ -269,6 +271,7 @@ public function testUploadLastChunkWithCallback() { $this->createFakeLocalFile('chunks/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt', '000-099'); + $file = UploadedFile::fake()->create('test.txt', 100); $request = Request::create('', Request::METHOD_POST, [ 'resumableChunkNumber' => 2, 'resumableTotalChunks' => 2, @@ -280,19 +283,19 @@ public function testUploadLastChunkWithCallback() 'resumableCurrentChunkSize' => 100, 'resumableType' => 'text/plain', ], [], [ - 'file' => UploadedFile::fake()->create('test.txt', 100), + 'file' => $file, ]); $callback = $this->createClosureMock( $this->once(), 'local', - 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt' + $file->hashName('merged') ); $this->handler->handle($request, $callback); - Event::assertDispatched(FileUploaded::class, function ($event) { - return $event->file = 'merged/200-0jWZTB1ZDfRQU6VTcXy0mJnL9xKMeEz3HoSPU0Zftxt.txt'; + Event::assertDispatched(FileUploaded::class, function ($event) use ($file) { + return $event->file = $file->hashName('merged'); }); } } diff --git a/tests/Identifier/SessionIdentifierTest.php b/tests/Identifier/SessionIdentifierTest.php index fd8bc0d..c6f3b83 100644 --- a/tests/Identifier/SessionIdentifierTest.php +++ b/tests/Identifier/SessionIdentifierTest.php @@ -47,6 +47,6 @@ public function testUploadedFileIdentifierNameWithoutExtension() { $file = UploadedFile::fake()->create('test', 100); $identifier = $this->identifier->generateUploadedFileIdentifierName($file); - $this->assertEquals('3b7b99bf70a98a544319cf3bad9e912e1b89984d', $identifier); + $this->assertEquals('3b7b99bf70a98a544319cf3bad9e912e1b89984d.bin', $identifier); } } From b2b1c997f906d0601ba16739b5d73104f1c743c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Wed, 25 Mar 2020 11:17:06 +0100 Subject: [PATCH 10/11] Rename parameters and change doc blocks --- src/Driver/BlueimpUploadDriver.php | 38 ++++++++------ src/Driver/DropzoneUploadDriver.php | 47 +++++++++-------- src/Driver/MonolithUploadDriver.php | 43 ++++++++------- src/Driver/ResumableJsUploadDriver.php | 52 +++++++++++-------- src/Driver/UploadDriver.php | 14 ++--- .../RequestEntityTooLargeHttpException.php | 5 +- tests/Driver/ResumableJsUploadDriverTest.php | 1 - 7 files changed, 112 insertions(+), 88 deletions(-) diff --git a/src/Driver/BlueimpUploadDriver.php b/src/Driver/BlueimpUploadDriver.php index d4d056e..ba839ec 100644 --- a/src/Driver/BlueimpUploadDriver.php +++ b/src/Driver/BlueimpUploadDriver.php @@ -32,6 +32,12 @@ class BlueimpUploadDriver extends UploadDriver */ private $identifier; + /** + * BlueimpUploadDriver constructor. + * + * @param array $config + * @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier + */ public function __construct($config, Identifier $identifier) { $this->fileParam = $config['param']; @@ -41,22 +47,22 @@ public function __construct($config, Identifier $identifier) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [Request::METHOD_HEAD, Request::METHOD_OPTIONS])) { return $this->info(); } if ($this->isRequestMethodIn($request, [Request::METHOD_GET])) { - return $this->download($request, $config); + return $this->download($request, $storageConfig); } if ($this->isRequestMethodIn($request, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH])) { - return $this->save($request, $config, $fileUploaded); + return $this->save($request, $storageConfig, $fileUploaded); } if ($this->isRequestMethodIn($request, [Request::METHOD_DELETE])) { - return $this->delete($request, $config); + return $this->delete($request, $storageConfig); } throw new MethodNotAllowedHttpException([ @@ -114,13 +120,13 @@ public function download(Request $request, StorageConfig $config) } /** - * @param \Illuminate\Http\Request $request - * @param StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); @@ -138,7 +144,7 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa $uuid = $this->identifier->generateUploadedFileIdentifierName($file); - $chunks = $this->storeChunk($config, $range, $file, $uuid); + $chunks = $this->storeChunk($storageConfig, $range, $file, $uuid); if (!$range->isLast()) { return new PercentageJsonResponse($range->getPercentage()); @@ -146,29 +152,29 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa $targetFilename = $file->hashName(); - $path = $this->mergeChunks($config, $chunks, $targetFilename); + $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); - if ($config->sweep()) { - $this->deleteChunkDirectory($config, $uuid); + if ($storageConfig->sweep()) { + $this->deleteChunkDirectory($storageConfig, $uuid); } - $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** * @param Request $request - * @param StorageConfig $config + * @param StorageConfig $storageConfig * * @return Response */ - public function delete(Request $request, StorageConfig $config) + public function delete(Request $request, StorageConfig $storageConfig) { $filename = $request->post($this->fileParam); - $path = $config->getMergedDirectory() . '/' . $filename; - Storage::disk($config->getDisk())->delete($path); + $path = $storageConfig->getMergedDirectory() . '/' . $filename; + Storage::disk($storageConfig->getDisk())->delete($path); return new Response(); } diff --git a/src/Driver/DropzoneUploadDriver.php b/src/Driver/DropzoneUploadDriver.php index a13282d..7d4ee5c 100644 --- a/src/Driver/DropzoneUploadDriver.php +++ b/src/Driver/DropzoneUploadDriver.php @@ -23,6 +23,11 @@ class DropzoneUploadDriver extends UploadDriver */ private $fileParam; + /** + * DropzoneUploadDriver constructor. + * + * @param array $config + */ public function __construct($config) { $this->fileParam = $config['param']; @@ -31,10 +36,10 @@ public function __construct($config) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [Request::METHOD_POST])) { - return $this->save($request, $config, $fileUploaded); + return $this->save($request, $storageConfig, $fileUploaded); } throw new MethodNotAllowedHttpException([ @@ -44,24 +49,24 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl /** * @param \Illuminate\Http\Request $request - * @param StorageConfig $config + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); $this->validateUploadedFile($file); if ($this->isMonolithRequest($request)) { - return $this->saveMonolith($file, $config, $fileUploaded); + return $this->saveMonolith($file, $storageConfig, $fileUploaded); } $this->validateChunkRequest($request); - return $this->saveChunk($file, $request, $config, $fileUploaded); + return $this->saveChunk($file, $request, $storageConfig, $fileUploaded); } /** @@ -95,32 +100,32 @@ private function validateChunkRequest(Request $request) } /** - * @param UploadedFile $file - * @param StorageConfig $config + * @param UploadedFile $uploadedFile + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return Response */ - private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure $fileUploaded = null): Response + private function saveMonolith(UploadedFile $uploadedFile, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { - $path = $file->store($config->getMergedDirectory(), [ - 'disk' => $config->getDisk(), + $path = $uploadedFile->store($storageConfig->getMergedDirectory(), [ + 'disk' => $storageConfig->getDisk(), ]); - $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** - * @param UploadedFile $file + * @param UploadedFile $uploadedFile * @param Request $request - * @param StorageConfig $config + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return Response */ - private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + private function saveChunk(UploadedFile $uploadedFile, Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { try { $range = new ZeroBasedRequestBodyRange( @@ -136,21 +141,21 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ $uuid = $request->post('dzuuid'); - $chunks = $this->storeChunk($config, $range, $file, $uuid); + $chunks = $this->storeChunk($storageConfig, $range, $uploadedFile, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $targetFilename = $file->hashName(); + $targetFilename = $uploadedFile->hashName(); - $path = $this->mergeChunks($config, $chunks, $targetFilename); + $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); - if ($config->sweep()) { - $this->deleteChunkDirectory($config, $uuid); + if ($storageConfig->sweep()) { + $this->deleteChunkDirectory($storageConfig, $uuid); } - $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } diff --git a/src/Driver/MonolithUploadDriver.php b/src/Driver/MonolithUploadDriver.php index fbe340f..190f013 100644 --- a/src/Driver/MonolithUploadDriver.php +++ b/src/Driver/MonolithUploadDriver.php @@ -17,6 +17,11 @@ class MonolithUploadDriver extends UploadDriver */ private $fileParam; + /** + * MonolithUploadDriver constructor. + * + * @param array $config + */ public function __construct($config) { $this->fileParam = $config['param']; @@ -25,18 +30,18 @@ public function __construct($config) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { if ($request->isMethod(Request::METHOD_POST)) { - return $this->save($request, $config, $fileUploaded); + return $this->save($request, $storageConfig, $fileUploaded); } if ($request->isMethod(Request::METHOD_GET)) { - return $this->download($request, $config); + return $this->download($request, $storageConfig); } if ($request->isMethod(Request::METHOD_DELETE)) { - return $this->delete($request, $config); + return $this->delete($request, $storageConfig); } throw new MethodNotAllowedHttpException([ @@ -47,52 +52,52 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl } /** - * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); $this->validateUploadedFile($file); - $path = $file->store($config->getMergedDirectory(), [ - 'disk' => $config->getDisk(), + $path = $file->store($storageConfig->getMergedDirectory(), [ + 'disk' => $storageConfig->getDisk(), ]); - $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** - * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * * @return \Symfony\Component\HttpFoundation\Response */ - public function download(Request $request, StorageConfig $config): Response + public function download(Request $request, StorageConfig $storageConfig): Response { $filename = $request->query($this->fileParam, $request->route($this->fileParam)); - return $this->fileResponse($filename, $config); + return $this->fileResponse($filename, $storageConfig); } /** - * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * * @return \Symfony\Component\HttpFoundation\Response */ - public function delete(Request $request, StorageConfig $config) + public function delete(Request $request, StorageConfig $storageConfig) { $filename = $request->post($this->fileParam, $request->route($this->fileParam)); - $path = $config->getMergedDirectory() . '/' . $filename; - Storage::disk($config->getDisk())->delete($path); + $path = $storageConfig->getMergedDirectory() . '/' . $filename; + Storage::disk($storageConfig->getDisk())->delete($path); return new Response(); } diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php index 8433f15..76145d0 100644 --- a/src/Driver/ResumableJsUploadDriver.php +++ b/src/Driver/ResumableJsUploadDriver.php @@ -45,6 +45,11 @@ class ResumableJsUploadDriver extends UploadDriver */ private $parameterNames; + /** + * ResumableJsUploadDriver constructor. + * + * @param array $config + */ public function __construct($config) { $this->fileParam = $config['param']; @@ -59,14 +64,14 @@ public function __construct($config) /** * @inheritDoc */ - public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [$this->testMethod])) { - return $this->resume($request, $config); + return $this->resume($request, $storageConfig); } if ($this->isRequestMethodIn($request, [$this->uploadMethod])) { - return $this->save($request, $config, $fileUploaded); + return $this->save($request, $storageConfig, $fileUploaded); } throw new MethodNotAllowedHttpException([ @@ -76,12 +81,12 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl } /** - * @param \Illuminate\Http\Request $request - * @param StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * * @return \Symfony\Component\HttpFoundation\Response */ - public function resume(Request $request, StorageConfig $config): Response + public function resume(Request $request, StorageConfig $storageConfig): Response { $this->validateChunkRequest($request); @@ -100,7 +105,7 @@ public function resume(Request $request, StorageConfig $config): Response $filename = $request->query($this->buildParameterName('identifier')); $chunkname = $this->buildChunkname($range); - if (! $this->chunkExists($config, $filename, $chunkname)) { + if (! $this->chunkExists($storageConfig, $filename, $chunkname)) { throw new NotFoundHttpException(); } @@ -108,13 +113,13 @@ public function resume(Request $request, StorageConfig $config): Response } /** - * @param \Illuminate\Http\Request $request - * @param StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); @@ -122,7 +127,7 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa $this->validateChunkRequest($request); - return $this->saveChunk($file, $request, $config, $fileUploaded); + return $this->saveChunk($file, $request, $storageConfig, $fileUploaded); } /** @@ -140,14 +145,14 @@ private function validateChunkRequest(Request $request) } /** - * @param UploadedFile $file + * @param UploadedFile $uploadedFile * @param Request $request - * @param StorageConfig $config + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return Response */ - private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response + private function saveChunk(UploadedFile $uploadedFile, Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response { try { $range = new OneBasedRequestBodyRange( @@ -163,26 +168,31 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $ $uuid = $request->post($this->buildParameterName('identifier')); - $chunks = $this->storeChunk($config, $range, $file, $uuid); + $chunks = $this->storeChunk($storageConfig, $range, $uploadedFile, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $targetFilename = $file->hashName(); + $targetFilename = $uploadedFile->hashName(); - $path = $this->mergeChunks($config, $chunks, $targetFilename); + $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); - if ($config->sweep()) { - $this->deleteChunkDirectory($config, $uuid); + if ($storageConfig->sweep()) { + $this->deleteChunkDirectory($storageConfig, $uuid); } - $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } - private function buildParameterName($key): string + /** + * @param $key string + * + * @return string + */ + private function buildParameterName(string $key): string { if (! array_key_exists($key, $this->parameterNames)) { throw new InvalidArgumentException(sprintf('`%s` is an invalid key for parameter name', $key)); diff --git a/src/Driver/UploadDriver.php b/src/Driver/UploadDriver.php index 9113a73..e253b8a 100644 --- a/src/Driver/UploadDriver.php +++ b/src/Driver/UploadDriver.php @@ -19,26 +19,26 @@ abstract class UploadDriver { /** - * @param \Illuminate\Http\Request $request - * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param Request $request + * @param StorageConfig $storageConfig * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response * */ - abstract public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response; + abstract public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response; /** * @param string $filename - * @param StorageConfig $config + * @param StorageConfig $storageConfig * * @return \Symfony\Component\HttpFoundation\Response */ - public function fileResponse(string $filename, StorageConfig $config): Response + public function fileResponse(string $filename, StorageConfig $storageConfig): Response { /** @var \Illuminate\Filesystem\FilesystemAdapter $disk */ - $disk = Storage::disk($config->getDisk()); - $prefix = $config->getMergedDirectory() . '/'; + $disk = Storage::disk($storageConfig->getDisk()); + $prefix = $storageConfig->getMergedDirectory() . '/'; if (! $disk->exists($prefix . $filename)) { throw new NotFoundHttpException($filename . ' file not found on server'); diff --git a/src/Exception/RequestEntityTooLargeHttpException.php b/src/Exception/RequestEntityTooLargeHttpException.php index 5cca512..13b733e 100644 --- a/src/Exception/RequestEntityTooLargeHttpException.php +++ b/src/Exception/RequestEntityTooLargeHttpException.php @@ -9,10 +9,9 @@ class RequestEntityTooLargeHttpException extends HttpException /** * RequestEntityTooLargeHttpException constructor. * - * @param null $message - * @param int $statusCode + * @param string|null $message * @param \Exception|null $previous - * @param int|null $code + * @param integer $code */ public function __construct($message = null, \Exception $previous = null, int $code = 0) { diff --git a/tests/Driver/ResumableJsUploadDriverTest.php b/tests/Driver/ResumableJsUploadDriverTest.php index 506111b..3672349 100644 --- a/tests/Driver/ResumableJsUploadDriverTest.php +++ b/tests/Driver/ResumableJsUploadDriverTest.php @@ -60,7 +60,6 @@ public function notAllowedRequestMethods() /** * @dataProvider notAllowedRequestMethods */ - public function testMethodNotAllowed($requestMethod) { $request = Request::create('', $requestMethod); From 6ce989f6319e18bb6f07e13ec6c1765a68be1c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6g?= Date: Thu, 26 Mar 2020 10:38:28 +0100 Subject: [PATCH 11/11] Use fully qualified names in doc blocks --- src/Driver/BlueimpUploadDriver.php | 44 +++++++++++--------- src/Driver/DropzoneUploadDriver.php | 56 +++++++++++++------------- src/Driver/MonolithUploadDriver.php | 38 ++++++++--------- src/Driver/ResumableJsUploadDriver.php | 48 +++++++++++----------- src/Driver/UploadDriver.php | 28 ++++++++++--- src/Helper/ChunkHelpers.php | 8 ++-- 6 files changed, 122 insertions(+), 100 deletions(-) diff --git a/src/Driver/BlueimpUploadDriver.php b/src/Driver/BlueimpUploadDriver.php index ba839ec..17be67a 100644 --- a/src/Driver/BlueimpUploadDriver.php +++ b/src/Driver/BlueimpUploadDriver.php @@ -47,22 +47,22 @@ public function __construct($config, Identifier $identifier) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [Request::METHOD_HEAD, Request::METHOD_OPTIONS])) { return $this->info(); } if ($this->isRequestMethodIn($request, [Request::METHOD_GET])) { - return $this->download($request, $storageConfig); + return $this->download($request, $config); } if ($this->isRequestMethodIn($request, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH])) { - return $this->save($request, $storageConfig, $fileUploaded); + return $this->save($request, $config, $fileUploaded); } if ($this->isRequestMethodIn($request, [Request::METHOD_DELETE])) { - return $this->delete($request, $storageConfig); + return $this->delete($request, $config); } throw new MethodNotAllowedHttpException([ @@ -90,7 +90,13 @@ public function info(): Response ]); } - public function download(Request $request, StorageConfig $config) + /** + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function download(Request $request, StorageConfig $config): Response { $download = $request->query('download', false); if ($download !== false) { @@ -120,13 +126,13 @@ public function download(Request $request, StorageConfig $config) } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); @@ -144,7 +150,7 @@ public function save(Request $request, StorageConfig $storageConfig, Closure $fi $uuid = $this->identifier->generateUploadedFileIdentifierName($file); - $chunks = $this->storeChunk($storageConfig, $range, $file, $uuid); + $chunks = $this->storeChunk($config, $range, $file, $uuid); if (!$range->isLast()) { return new PercentageJsonResponse($range->getPercentage()); @@ -152,29 +158,29 @@ public function save(Request $request, StorageConfig $storageConfig, Closure $fi $targetFilename = $file->hashName(); - $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); + $path = $this->mergeChunks($config, $chunks, $targetFilename); - if ($storageConfig->sweep()) { - $this->deleteChunkDirectory($storageConfig, $uuid); + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $uuid); } - $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * - * @return Response + * @return \Symfony\Component\HttpFoundation\Response */ - public function delete(Request $request, StorageConfig $storageConfig) + public function delete(Request $request, StorageConfig $config): Response { $filename = $request->post($this->fileParam); - $path = $storageConfig->getMergedDirectory() . '/' . $filename; - Storage::disk($storageConfig->getDisk())->delete($path); + $path = $config->getMergedDirectory() . '/' . $filename; + Storage::disk($config->getDisk())->delete($path); return new Response(); } diff --git a/src/Driver/DropzoneUploadDriver.php b/src/Driver/DropzoneUploadDriver.php index 7d4ee5c..8660981 100644 --- a/src/Driver/DropzoneUploadDriver.php +++ b/src/Driver/DropzoneUploadDriver.php @@ -36,10 +36,10 @@ public function __construct($config) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [Request::METHOD_POST])) { - return $this->save($request, $storageConfig, $fileUploaded); + return $this->save($request, $config, $fileUploaded); } throw new MethodNotAllowedHttpException([ @@ -49,32 +49,32 @@ public function handle(Request $request, StorageConfig $storageConfig, Closure $ /** * @param \Illuminate\Http\Request $request - * @param StorageConfig $storageConfig + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); $this->validateUploadedFile($file); if ($this->isMonolithRequest($request)) { - return $this->saveMonolith($file, $storageConfig, $fileUploaded); + return $this->saveMonolith($file, $config, $fileUploaded); } $this->validateChunkRequest($request); - return $this->saveChunk($file, $request, $storageConfig, $fileUploaded); + return $this->saveChunk($file, $request, $config, $fileUploaded); } /** - * @param Request $request + * @param \Illuminate\Http\Request $request * * @return bool */ - private function isMonolithRequest(Request $request) + private function isMonolithRequest(Request $request): bool { return $request->post('dzuuid') === null && $request->post('dzchunkindex') === null @@ -85,9 +85,9 @@ private function isMonolithRequest(Request $request) } /** - * @param Request $request + * @param \Illuminate\Http\Request $request */ - private function validateChunkRequest(Request $request) + private function validateChunkRequest(Request $request): void { $request->validate([ 'dzuuid' => 'required', @@ -100,32 +100,32 @@ private function validateChunkRequest(Request $request) } /** - * @param UploadedFile $uploadedFile - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\UploadedFile $file + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * - * @return Response + * @return \Symfony\Component\HttpFoundation\Response */ - private function saveMonolith(UploadedFile $uploadedFile, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure $fileUploaded = null): Response { - $path = $uploadedFile->store($storageConfig->getMergedDirectory(), [ - 'disk' => $storageConfig->getDisk(), + $path = $file->store($config->getMergedDirectory(), [ + 'disk' => $config->getDisk(), ]); - $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** - * @param UploadedFile $uploadedFile - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\UploadedFile $file + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * - * @return Response + * @return \Symfony\Component\HttpFoundation\Response */ - private function saveChunk(UploadedFile $uploadedFile, Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { try { $range = new ZeroBasedRequestBodyRange( @@ -141,21 +141,21 @@ private function saveChunk(UploadedFile $uploadedFile, Request $request, Storage $uuid = $request->post('dzuuid'); - $chunks = $this->storeChunk($storageConfig, $range, $uploadedFile, $uuid); + $chunks = $this->storeChunk($config, $range, $file, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $targetFilename = $uploadedFile->hashName(); + $targetFilename = $file->hashName(); - $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); + $path = $this->mergeChunks($config, $chunks, $targetFilename); - if ($storageConfig->sweep()) { - $this->deleteChunkDirectory($storageConfig, $uuid); + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $uuid); } - $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } diff --git a/src/Driver/MonolithUploadDriver.php b/src/Driver/MonolithUploadDriver.php index 190f013..7289bac 100644 --- a/src/Driver/MonolithUploadDriver.php +++ b/src/Driver/MonolithUploadDriver.php @@ -30,18 +30,18 @@ public function __construct($config) /** * {@inheritDoc} */ - public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { if ($request->isMethod(Request::METHOD_POST)) { - return $this->save($request, $storageConfig, $fileUploaded); + return $this->save($request, $config, $fileUploaded); } if ($request->isMethod(Request::METHOD_GET)) { - return $this->download($request, $storageConfig); + return $this->download($request, $config); } if ($request->isMethod(Request::METHOD_DELETE)) { - return $this->delete($request, $storageConfig); + return $this->delete($request, $config); } throw new MethodNotAllowedHttpException([ @@ -52,52 +52,52 @@ public function handle(Request $request, StorageConfig $storageConfig, Closure $ } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); $this->validateUploadedFile($file); - $path = $file->store($storageConfig->getMergedDirectory(), [ - 'disk' => $storageConfig->getDisk(), + $path = $file->store($config->getMergedDirectory(), [ + 'disk' => $config->getDisk(), ]); - $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * * @return \Symfony\Component\HttpFoundation\Response */ - public function download(Request $request, StorageConfig $storageConfig): Response + public function download(Request $request, StorageConfig $config): Response { $filename = $request->query($this->fileParam, $request->route($this->fileParam)); - return $this->fileResponse($filename, $storageConfig); + return $this->fileResponse($filename, $config); } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * * @return \Symfony\Component\HttpFoundation\Response */ - public function delete(Request $request, StorageConfig $storageConfig) + public function delete(Request $request, StorageConfig $config): Response { $filename = $request->post($this->fileParam, $request->route($this->fileParam)); - $path = $storageConfig->getMergedDirectory() . '/' . $filename; - Storage::disk($storageConfig->getDisk())->delete($path); + $path = $config->getMergedDirectory() . '/' . $filename; + Storage::disk($config->getDisk())->delete($path); return new Response(); } diff --git a/src/Driver/ResumableJsUploadDriver.php b/src/Driver/ResumableJsUploadDriver.php index 76145d0..d4b0139 100644 --- a/src/Driver/ResumableJsUploadDriver.php +++ b/src/Driver/ResumableJsUploadDriver.php @@ -64,14 +64,14 @@ public function __construct($config) /** * @inheritDoc */ - public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { if ($this->isRequestMethodIn($request, [$this->testMethod])) { - return $this->resume($request, $storageConfig); + return $this->resume($request, $config); } if ($this->isRequestMethodIn($request, [$this->uploadMethod])) { - return $this->save($request, $storageConfig, $fileUploaded); + return $this->save($request, $config, $fileUploaded); } throw new MethodNotAllowedHttpException([ @@ -81,12 +81,12 @@ public function handle(Request $request, StorageConfig $storageConfig, Closure $ } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * * @return \Symfony\Component\HttpFoundation\Response */ - public function resume(Request $request, StorageConfig $storageConfig): Response + public function resume(Request $request, StorageConfig $config): Response { $this->validateChunkRequest($request); @@ -105,7 +105,7 @@ public function resume(Request $request, StorageConfig $storageConfig): Response $filename = $request->query($this->buildParameterName('identifier')); $chunkname = $this->buildChunkname($range); - if (! $this->chunkExists($storageConfig, $filename, $chunkname)) { + if (! $this->chunkExists($config, $filename, $chunkname)) { throw new NotFoundHttpException(); } @@ -113,13 +113,13 @@ public function resume(Request $request, StorageConfig $storageConfig): Response } /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response */ - public function save(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + public function save(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { $file = $request->file($this->fileParam); @@ -127,13 +127,13 @@ public function save(Request $request, StorageConfig $storageConfig, Closure $fi $this->validateChunkRequest($request); - return $this->saveChunk($file, $request, $storageConfig, $fileUploaded); + return $this->saveChunk($file, $request, $config, $fileUploaded); } /** - * @param Request $request + * @param \Illuminate\Http\Request $request */ - private function validateChunkRequest(Request $request) + private function validateChunkRequest(Request $request): void { $validation = []; @@ -145,14 +145,14 @@ private function validateChunkRequest(Request $request) } /** - * @param UploadedFile $uploadedFile - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\UploadedFile $file + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * - * @return Response + * @return \Symfony\Component\HttpFoundation\Response */ - private function saveChunk(UploadedFile $uploadedFile, Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response + private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response { try { $range = new OneBasedRequestBodyRange( @@ -168,21 +168,21 @@ private function saveChunk(UploadedFile $uploadedFile, Request $request, Storage $uuid = $request->post($this->buildParameterName('identifier')); - $chunks = $this->storeChunk($storageConfig, $range, $uploadedFile, $uuid); + $chunks = $this->storeChunk($config, $range, $file, $uuid); if (!$range->isFinished($chunks)) { return new PercentageJsonResponse($range->getPercentage($chunks)); } - $targetFilename = $uploadedFile->hashName(); + $targetFilename = $file->hashName(); - $path = $this->mergeChunks($storageConfig, $chunks, $targetFilename); + $path = $this->mergeChunks($config, $chunks, $targetFilename); - if ($storageConfig->sweep()) { - $this->deleteChunkDirectory($storageConfig, $uuid); + if ($config->sweep()) { + $this->deleteChunkDirectory($config, $uuid); } - $this->triggerFileUploadedEvent($storageConfig->getDisk(), $path, $fileUploaded); + $this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded); return new PercentageJsonResponse(100); } diff --git a/src/Driver/UploadDriver.php b/src/Driver/UploadDriver.php index e253b8a..ef11778 100644 --- a/src/Driver/UploadDriver.php +++ b/src/Driver/UploadDriver.php @@ -19,18 +19,18 @@ abstract class UploadDriver { /** - * @param Request $request - * @param StorageConfig $storageConfig + * @param \Illuminate\Http\Request $request + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param \Closure|null $fileUploaded * * @return \Symfony\Component\HttpFoundation\Response * */ - abstract public function handle(Request $request, StorageConfig $storageConfig, Closure $fileUploaded = null): Response; + abstract public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response; /** * @param string $filename - * @param StorageConfig $storageConfig + * @param \LaraCrafts\ChunkUploader\StorageConfig $storageConfig * * @return \Symfony\Component\HttpFoundation\Response */ @@ -49,6 +49,14 @@ public function fileResponse(string $filename, StorageConfig $storageConfig): Re return new BinaryFileResponse($path, 200, [], true, ResponseHeaderBag::DISPOSITION_ATTACHMENT); } + /** + * Check if the request type of the given request is in the specified list. + * + * @param \Illuminate\Http\Request $request + * @param array $methods + * + * @return bool + */ public function isRequestMethodIn(Request $request, array $methods): bool { foreach ($methods as $method) { @@ -60,7 +68,15 @@ public function isRequestMethodIn(Request $request, array $methods): bool return false; } - protected function triggerFileUploadedEvent($disk, $path, Closure $fileUploaded = null) + /** + * Dispatch a {@link \LaraCrafts\ChunkUploader\Event\FileUploaded} event. + * Also call the given {@link \Closure} if not null. + * + * @param $disk + * @param $path + * @param \Closure|null $fileUploaded + */ + protected function triggerFileUploadedEvent($disk, $path, Closure $fileUploaded = null): void { if ($fileUploaded !== null) { $fileUploaded($disk, $path); @@ -77,7 +93,7 @@ protected function triggerFileUploadedEvent($disk, $path, Closure $fileUploaded * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException when given file is null. * @throws \LaraCrafts\ChunkUploader\Exception\InternalServerErrorHttpException when given file is invalid. */ - protected function validateUploadedFile(UploadedFile $file = null) + protected function validateUploadedFile(UploadedFile $file = null): void { if (null === $file) { throw new BadRequestHttpException('File not found in request body'); diff --git a/src/Helper/ChunkHelpers.php b/src/Helper/ChunkHelpers.php index 6ffc594..37f6979 100644 --- a/src/Helper/ChunkHelpers.php +++ b/src/Helper/ChunkHelpers.php @@ -18,7 +18,7 @@ trait ChunkHelpers /** * Combine all the given chunks to one single file with the given filename. * - * @param StorageConfig $config + * @param \LaraCrafts\ChunkUploader\StorageConfig $config * @param array $chunks * @param string $targetFilename * @@ -63,9 +63,9 @@ public function deleteChunkDirectory(StorageConfig $config, string $uuid): void /** * Persist an uploaded chunk in a directory with the given name in the chunk directory. * - * @param StorageConfig $config - * @param Range $range - * @param UploadedFile $file + * @param \LaraCrafts\ChunkUploader\StorageConfig $config + * @param \LaraCrafts\ChunkUploader\Range\Range $range + * @param \Illuminate\Http\UploadedFile $file * @param string $uuid * @return array */