Skip to content

Commit 029d325

Browse files
nerg4laxlon
andauthored
Add Resumable.js driver (#36)
* Add Resumable.js driver * Separate logic of request body range depending on base * Update config/chunk-uploader.php Co-Authored-By: Choraimy Kroonstuiver <choraimy@23g.nl> * Use hash name for target filename Co-Authored-By: Choraimy Kroonstuiver <choraimy@23g.nl> * Use fully qualified names in doc blocks Co-authored-by: Choraimy Kroonstuiver <choraimy@23g.nl>
1 parent c374450 commit 029d325

20 files changed

+1144
-172
lines changed

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ project at the moment is [tus](https://tus.io/).
3636
- [Monolith](#monolith-driver)
3737
- [Blueimp](#blueimp-driver)
3838
- [DropzoneJS](#dropzonejs-driver)
39+
- [Resumable.js](#resumable-js-driver)
3940
- [Identifiers](#identifiers)
4041
- [Session identifier](#session-identifier)
4142
- [Contribution](#contribution)
@@ -72,7 +73,7 @@ class MyController extends Controller
7273
{
7374
public function myFunction(Request $request, UploadHandler $handler)
7475
{
75-
$handler->handle($request);
76+
return $handler->handle($request);
7677
}
7778
}
7879
```
@@ -86,7 +87,7 @@ class MyController extends Controller
8687
public function myFunction(Request $request)
8788
{
8889
$handler = app()->make(UploadHandler::class);
89-
$handler->handle($request);
90+
return $handler->handle($request);
9091
}
9192
}
9293
```
@@ -142,11 +143,12 @@ If you wrote a custom driver that others might find useful, please consider addi
142143

143144
Below is a list of available drivers along with their individual specs:
144145

145-
Service | Driver name | Chunk upload | Resumable
146-
---------------------------------|-------------|--------------|-----------
147-
[Monolith](#monolith-driver) | `monolith` | no | no
148-
[Blueimp](#blueimp-driver) | `blueimp` | yes | yes
149-
[DropzoneJS](#dropzonejs-driver) | `dropzone` | yes | no
146+
Service | Driver name | Chunk upload | Resumable
147+
-------------------------------------|----------------|--------------|-----------
148+
[Monolith](#monolith-driver) | `monolith` | no | no
149+
[Blueimp](#blueimp-driver) | `blueimp` | yes | yes
150+
[DropzoneJS](#dropzonejs-driver) | `dropzone` | yes | no
151+
[Resumable.js](#resumable-js-driver) | `resumable-js` | yes | yes
150152

151153
### Monolith driver
152154

@@ -164,6 +166,12 @@ This driver handles requests made by the Blueimp jQuery File Upload client libra
164166

165167
This driver handles requests made by the DropzoneJS client library.
166168

169+
### Resumable.js driver
170+
171+
[website](http://resumablejs.com/)
172+
173+
This driver handles requests made by the Resumable.js client library.
174+
167175
## Identifiers
168176

169177
In some cases an identifier is needed for the uploaded file when the client side library does not provide one.

config/chunk-uploader.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
| throughout your application here. By default, the module is setup for
1313
| monolith upload.
1414
|
15-
| Supported: "monolith", "blueimp", "dropzone"
15+
| Supported: "monolith", "blueimp", "dropzone", "resumable-js"
1616
|
1717
*/
1818

@@ -118,4 +118,49 @@
118118

119119
],
120120

121+
/*
122+
|--------------------------------------------------------------------------
123+
| Resumable.js Options
124+
|--------------------------------------------------------------------------
125+
|
126+
| Here you may configure the options for the Resumable.js driver.
127+
|
128+
*/
129+
130+
'resumable-js' => [
131+
132+
// The name of the multipart request parameter to use for the file chunk
133+
'param' => 'file',
134+
135+
// HTTP method for chunk test request.
136+
'test-method' => Illuminate\Http\Request::METHOD_GET,
137+
// HTTP method to use when sending chunks to the server (POST, PUT, PATCH).
138+
'upload-method' => Illuminate\Http\Request::METHOD_POST,
139+
140+
// Extra prefix added before the name of each parameter included in the multipart POST or in the test GET.
141+
'parameter-namespace' => '',
142+
143+
'parameter-names' => [
144+
// The name of the chunk index (base-1) in the current upload POST parameter to use for the file chunk.
145+
'chunk-number' => 'resumableChunkNumber',
146+
// The name of the total number of chunks POST parameter to use for the file chunk.
147+
'total-chunks' => 'resumableTotalChunks',
148+
// The name of the general chunk size POST parameter to use for the file chunk.
149+
'chunk-size' => 'resumableChunkSize',
150+
// The name of the total file size number POST parameter to use for the file chunk.
151+
'total-size' => 'resumableTotalSize',
152+
// The name of the unique identifier POST parameter to use for the file chunk.
153+
'identifier' => 'resumableIdentifier',
154+
// The name of the original file name POST parameter to use for the file chunk.
155+
'file-name' => 'resumableFilename',
156+
// The name of the file's relative path POST parameter to use for the file chunk.
157+
'relative-path' => 'resumableRelativePath',
158+
// The name of the current chunk size POST parameter to use for the file chunk.
159+
'current-chunk-size' => 'resumableCurrentChunkSize',
160+
// The name of the file type POST parameter to use for the file chunk.
161+
'type' => 'resumableType',
162+
],
163+
164+
],
165+
121166
];

src/Driver/BlueimpUploadDriver.php

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class BlueimpUploadDriver extends UploadDriver
3232
*/
3333
private $identifier;
3434

35+
/**
36+
* BlueimpUploadDriver constructor.
37+
*
38+
* @param array $config
39+
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
40+
*/
3541
public function __construct($config, Identifier $identifier)
3642
{
3743
$this->fileParam = $config['param'];
@@ -84,7 +90,13 @@ public function info(): Response
8490
]);
8591
}
8692

87-
public function download(Request $request, StorageConfig $config)
93+
/**
94+
* @param \Illuminate\Http\Request $request
95+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
96+
*
97+
* @return \Symfony\Component\HttpFoundation\Response
98+
*/
99+
public function download(Request $request, StorageConfig $config): Response
88100
{
89101
$download = $request->query('download', false);
90102
if ($download !== false) {
@@ -93,18 +105,16 @@ public function download(Request $request, StorageConfig $config)
93105
return $this->fileResponse($filename, $config);
94106
}
95107

108+
$request->validate([$this->fileParam => 'required']);
96109
$filename = $request->query($this->fileParam);
97-
$directory = $config->getChunkDirectory() . '/' . $filename;
98-
/** @var \Illuminate\Filesystem\FilesystemAdapter $disk */
99-
$disk = Storage::disk($config->getDisk());
100110

101-
if (! $disk->exists($directory)) {
111+
if (!$this->chunkExists($config, $filename)) {
102112
return new JsonResponse([
103113
'file' => null,
104114
]);
105115
}
106116

107-
$chunk = Arr::last($disk->files($directory));
117+
$chunk = Arr::last($this->chunks($config, $filename));
108118
$size = explode('-', basename($chunk))[1] + 1;
109119

110120
return new JsonResponse([
@@ -117,8 +127,7 @@ public function download(Request $request, StorageConfig $config)
117127

118128
/**
119129
* @param \Illuminate\Http\Request $request
120-
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
121-
* @param StorageConfig $config
130+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
122131
* @param \Closure|null $fileUploaded
123132
*
124133
* @return \Symfony\Component\HttpFoundation\Response
@@ -139,18 +148,20 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa
139148
throw new BadRequestHttpException($e->getMessage(), $e);
140149
}
141150

142-
$filename = $this->identifier->generateUploadedFileIdentifierName($file);
151+
$uuid = $this->identifier->generateUploadedFileIdentifierName($file);
143152

144-
$chunks = $this->storeChunk($config, $range, $file, $filename);
153+
$chunks = $this->storeChunk($config, $range, $file, $uuid);
145154

146-
if (! $range->isLast()) {
155+
if (!$range->isLast()) {
147156
return new PercentageJsonResponse($range->getPercentage());
148157
}
149158

150-
$path = $this->mergeChunks($config, $chunks, $filename);
159+
$targetFilename = $file->hashName();
160+
161+
$path = $this->mergeChunks($config, $chunks, $targetFilename);
151162

152-
if (! empty($config->sweep())) {
153-
Storage::disk($config->getDisk())->deleteDirectory($filename);
163+
if ($config->sweep()) {
164+
$this->deleteChunkDirectory($config, $uuid);
154165
}
155166

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

161172
/**
162-
* @param Request $request
163-
* @param StorageConfig $config
164-
* @return Response
173+
* @param \Illuminate\Http\Request $request
174+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
175+
*
176+
* @return \Symfony\Component\HttpFoundation\Response
165177
*/
166-
public function delete(Request $request, StorageConfig $config)
178+
public function delete(Request $request, StorageConfig $config): Response
167179
{
168180
$filename = $request->post($this->fileParam);
169181

src/Driver/DropzoneUploadDriver.php

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
use Closure;
66
use Illuminate\Http\Request;
77
use Illuminate\Http\UploadedFile;
8-
use Illuminate\Support\Facades\Storage;
98
use InvalidArgumentException;
109
use LaraCrafts\ChunkUploader\Helper\ChunkHelpers;
11-
use LaraCrafts\ChunkUploader\Identifier\Identifier;
12-
use LaraCrafts\ChunkUploader\Range\RequestBodyRange;
10+
use LaraCrafts\ChunkUploader\Range\ZeroBasedRequestBodyRange;
1311
use LaraCrafts\ChunkUploader\Response\PercentageJsonResponse;
1412
use LaraCrafts\ChunkUploader\StorageConfig;
1513
use Symfony\Component\HttpFoundation\Response;
@@ -25,6 +23,11 @@ class DropzoneUploadDriver extends UploadDriver
2523
*/
2624
private $fileParam;
2725

26+
/**
27+
* DropzoneUploadDriver constructor.
28+
*
29+
* @param array $config
30+
*/
2831
public function __construct($config)
2932
{
3033
$this->fileParam = $config['param'];
@@ -46,8 +49,7 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl
4649

4750
/**
4851
* @param \Illuminate\Http\Request $request
49-
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
50-
* @param StorageConfig $config
52+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
5153
* @param \Closure|null $fileUploaded
5254
*
5355
* @return \Symfony\Component\HttpFoundation\Response
@@ -68,11 +70,11 @@ public function save(Request $request, StorageConfig $config, Closure $fileUploa
6870
}
6971

7072
/**
71-
* @param Request $request
73+
* @param \Illuminate\Http\Request $request
7274
*
7375
* @return bool
7476
*/
75-
private function isMonolithRequest(Request $request)
77+
private function isMonolithRequest(Request $request): bool
7678
{
7779
return $request->post('dzuuid') === null
7880
&& $request->post('dzchunkindex') === null
@@ -83,9 +85,9 @@ private function isMonolithRequest(Request $request)
8385
}
8486

8587
/**
86-
* @param Request $request
88+
* @param \Illuminate\Http\Request $request
8789
*/
88-
private function validateChunkRequest(Request $request)
90+
private function validateChunkRequest(Request $request): void
8991
{
9092
$request->validate([
9193
'dzuuid' => 'required',
@@ -98,12 +100,11 @@ private function validateChunkRequest(Request $request)
98100
}
99101

100102
/**
101-
* @param UploadedFile $file
102-
* @param Identifier $identifier
103-
* @param StorageConfig $config
103+
* @param \Illuminate\Http\UploadedFile $file
104+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
104105
* @param \Closure|null $fileUploaded
105106
*
106-
* @return Response
107+
* @return \Symfony\Component\HttpFoundation\Response
107108
*/
108109
private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure $fileUploaded = null): Response
109110
{
@@ -117,17 +118,17 @@ private function saveMonolith(UploadedFile $file, StorageConfig $config, Closure
117118
}
118119

119120
/**
120-
* @param UploadedFile $file
121-
* @param Request $request
122-
* @param StorageConfig $config
121+
* @param \Illuminate\Http\UploadedFile $file
122+
* @param \Illuminate\Http\Request $request
123+
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
123124
* @param \Closure|null $fileUploaded
124125
*
125-
* @return Response
126+
* @return \Symfony\Component\HttpFoundation\Response
126127
*/
127128
private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response
128129
{
129130
try {
130-
$range = new RequestBodyRange(
131+
$range = new ZeroBasedRequestBodyRange(
131132
$request,
132133
'dzchunkindex',
133134
'dztotalchunkcount',
@@ -138,23 +139,20 @@ private function saveChunk(UploadedFile $file, Request $request, StorageConfig $
138139
throw new BadRequestHttpException($e->getMessage(), $e);
139140
}
140141

141-
$filename = $request->post('dzuuid');
142+
$uuid = $request->post('dzuuid');
142143

143-
// On windows you can not create a file whose name ends with a dot
144-
if ($file->getClientOriginalExtension()) {
145-
$filename .= '.' . $file->getClientOriginalExtension();
146-
}
147-
148-
$chunks = $this->storeChunk($config, $range, $file, $filename);
144+
$chunks = $this->storeChunk($config, $range, $file, $uuid);
149145

150146
if (!$range->isFinished($chunks)) {
151147
return new PercentageJsonResponse($range->getPercentage($chunks));
152148
}
153149

154-
$path = $this->mergeChunks($config, $chunks, $filename);
150+
$targetFilename = $file->hashName();
151+
152+
$path = $this->mergeChunks($config, $chunks, $targetFilename);
155153

156-
if (!empty($config->sweep())) {
157-
Storage::disk($config->getDisk())->deleteDirectory($filename);
154+
if ($config->sweep()) {
155+
$this->deleteChunkDirectory($config, $uuid);
158156
}
159157

160158
$this->triggerFileUploadedEvent($config->getDisk(), $path, $fileUploaded);

src/Driver/MonolithUploadDriver.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class MonolithUploadDriver extends UploadDriver
1717
*/
1818
private $fileParam;
1919

20+
/**
21+
* MonolithUploadDriver constructor.
22+
*
23+
* @param array $config
24+
*/
2025
public function __construct($config)
2126
{
2227
$this->fileParam = $config['param'];
@@ -48,7 +53,6 @@ public function handle(Request $request, StorageConfig $config, Closure $fileUpl
4853

4954
/**
5055
* @param \Illuminate\Http\Request $request
51-
* @param \LaraCrafts\ChunkUploader\Identifier\Identifier $identifier
5256
* @param \LaraCrafts\ChunkUploader\StorageConfig $config
5357
* @param \Closure|null $fileUploaded
5458
*
@@ -88,7 +92,7 @@ public function download(Request $request, StorageConfig $config): Response
8892
*
8993
* @return \Symfony\Component\HttpFoundation\Response
9094
*/
91-
public function delete(Request $request, StorageConfig $config)
95+
public function delete(Request $request, StorageConfig $config): Response
9296
{
9397
$filename = $request->post($this->fileParam, $request->route($this->fileParam));
9498

0 commit comments

Comments
 (0)