Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -72,7 +73,7 @@ class MyController extends Controller
{
public function myFunction(Request $request, UploadHandler $handler)
{
$handler->handle($request);
return $handler->handle($request);
}
}
```
Expand All @@ -86,7 +87,7 @@ class MyController extends Controller
public function myFunction(Request $request)
{
$handler = app()->make(UploadHandler::class);
$handler->handle($request);
return $handler->handle($request);
}
}
```
Expand Down Expand Up @@ -142,11 +143,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

Expand All @@ -164,6 +166,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.
Expand Down
47 changes: 46 additions & 1 deletion config/chunk-uploader.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"
|
*/

Expand Down Expand Up @@ -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',
],

],

];
48 changes: 30 additions & 18 deletions src/Driver/BlueimpUploadDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down Expand Up @@ -84,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) {
Expand All @@ -93,18 +105,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([
Expand All @@ -117,8 +127,7 @@ public function download(Request $request, StorageConfig $config)

/**
* @param \Illuminate\Http\Request $request
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
* @param StorageConfig $config
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
* @param \Closure|null $fileUploaded
*
* @return \Symfony\Component\HttpFoundation\Response
Expand All @@ -139,18 +148,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 (! empty($config->sweep())) {
Storage::disk($config->getDisk())->deleteDirectory($filename);
if ($config->sweep()) {
$this->deleteChunkDirectory($config, $uuid);
}

$this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded);
Expand All @@ -159,11 +170,12 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa
}

/**
* @param Request $request
* @param StorageConfig $config
* @return Response
* @param \Illuminate\Http\Request $request
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function delete(Request $request, StorageConfig $config)
public function delete(Request $request, StorageConfig $config): Response
{
$filename = $request->post($this->fileParam);

Expand Down
54 changes: 26 additions & 28 deletions src/Driver/DropzoneUploadDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
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\Range\ZeroBasedRequestBodyRange;
use LaraCrafts\ChunkUploader\Response\PercentageJsonResponse;
use LaraCrafts\ChunkUploader\StorageConfig;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -25,6 +23,11 @@ class DropzoneUploadDriver extends UploadDriver
*/
private $fileParam;

/**
* DropzoneUploadDriver constructor.
*
* @param array $config
*/
public function __construct($config)
{
$this->fileParam = $config['param'];
Expand All @@ -46,8 +49,7 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl

/**
* @param \Illuminate\Http\Request $request
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
* @param StorageConfig $config
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
* @param \Closure|null $fileUploaded
*
* @return \Symfony\Component\HttpFoundation\Response
Expand All @@ -68,11 +70,11 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa
}

/**
* @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
Expand All @@ -83,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',
Expand All @@ -98,12 +100,11 @@ private function validateChunkRequest(Request $request)
}

/**
* @param UploadedFile $file
* @param Identifier $identifier
* @param StorageConfig $config
* @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 $file, StorageConfig $config, Closure $fileUploaded = null): Response
{
Expand All @@ -117,17 +118,17 @@ private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure
}

/**
* @param UploadedFile $file
* @param Request $request
* @param StorageConfig $config
* @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 $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response
{
try {
$range = new RequestBodyRange(
$range = new ZeroBasedRequestBodyRange(
$request,
'dzchunkindex',
'dztotalchunkcount',
Expand All @@ -138,23 +139,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 (!empty($config->sweep())) {
Storage::disk($config->getDisk())->deleteDirectory($filename);
if ($config->sweep()) {
$this->deleteChunkDirectory($config, $uuid);
}

$this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded);
Expand Down
8 changes: 6 additions & 2 deletions src/Driver/MonolithUploadDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ class MonolithUploadDriver extends UploadDriver
*/
private $fileParam;

/**
* MonolithUploadDriver constructor.
*
* @param array $config
*/
public function __construct($config)
{
$this->fileParam = $config['param'];
Expand Down Expand Up @@ -48,7 +53,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
*
Expand Down Expand Up @@ -88,7 +92,7 @@ public function download(Request $request, StorageConfig $config): Response
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function delete(Request $request, StorageConfig $config)
public function delete(Request $request, StorageConfig $config): Response
{
$filename = $request->post($this->fileParam, $request->route($this->fileParam));

Expand Down
Loading