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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ project at the moment is [tus](https://tus.io/).
- [Blueimp](#blueimp-driver)
- [DropzoneJS](#dropzonejs-driver)
- [Flow.js](#flow-js-driver)
- [Plupload](#plupload-driver)
- [Resumable.js](#resumable-js-driver)
- [simple-uploader.js](#simple-uploader-js-driver)
- [Identifiers](#identifiers)
Expand Down Expand Up @@ -152,6 +153,7 @@ Service | Driver name | Chunk
[Blueimp](#blueimp-driver) | `blueimp` | yes | yes
[DropzoneJS](#dropzonejs-driver) | `dropzone` | yes | no
[Flow.js](#flow-js-driver) | `flow-js` | yes | yes
[Plupload](#plupload-driver) | `plupload` | yes | no
[Resumable.js](#resumable-js-driver) | `resumable-js` | yes | yes
[simple-uploader.js](#simple-uploader-js-driver) | `simple-uploader-js` | yes | yes

Expand All @@ -177,6 +179,12 @@ This driver handles requests made by the DropzoneJS client library.

This driver handles requests made by the Flow.js client library.

### Plupload driver

[website](https://github.com/moxiecode/plupload)

This driver handles requests made by the Plupload client library.

### Resumable.js driver

[website](http://resumablejs.com/)
Expand Down
60 changes: 60 additions & 0 deletions examples/plupload.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Plupload</title>
</head>
<body>
<h1>Plupload</h1>

<div id="container">
<a id="pickfiles" href="javascript:void(0);">[Select files]</a>
<a id="uploadfiles" href="javascript:void(0);">[Upload files]</a>
</div>

<ul id="filelist">
<li>Your browser doesn't have HTML5 support.</li>
</ul>

<script src="//cdn.jsdelivr.net/gh/moxiecode/plupload/js/plupload.full.min.js"></script>
<script>
function getCookieValue(a) {
const b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
return b ? b.pop() : '';
}

const uploadUrl = "{{ url('upload') }}";
const uploader = new plupload.Uploader({
browse_button: 'pickfiles',
container: document.getElementById('container'),
url: uploadUrl,
chunk_size: 1024 * 1024,

headers: {'X-XSRF-TOKEN': decodeURIComponent(getCookieValue('XSRF-TOKEN'))},

init: {
PostInit: function () {
document.getElementById('filelist').innerHTML = '';

document.getElementById('uploadfiles').onclick = () => {
uploader.start();
return false;
};
},

FilesAdded: function (up, files) {
plupload.each(files, function (file) {
const node = document.createElement('li');
node.innerHTML = file.name + ' (' + plupload.formatSize(file.size) + ')';
document.getElementById('filelist').append(node);
});
},
}
});

uploader.init();
</script>
</body>
</html>
118 changes: 118 additions & 0 deletions src/Driver/PluploadUploadDriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

namespace CodingSocks\ChunkUploader\Driver;

use Closure;
use CodingSocks\ChunkUploader\Helper\ChunkHelpers;
use CodingSocks\ChunkUploader\Identifier\Identifier;
use CodingSocks\ChunkUploader\Range\PluploadRange;
use CodingSocks\ChunkUploader\Response\PercentageJsonResponse;
use CodingSocks\ChunkUploader\StorageConfig;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;

class PluploadUploadDriver extends UploadDriver
{
use ChunkHelpers;

/**
* @var \CodingSocks\ChunkUploader\Identifier\Identifier
*/
private $identifier;

/**
* PluploadUploadDriver constructor.
*
* @param \CodingSocks\ChunkUploader\Identifier\Identifier $identifier
*/
public function __construct(Identifier $identifier)
{
$this->identifier = $identifier;
}


/**
* @inheritDoc
*/
public function handle(Request $request, StorageConfig $config, Closure $fileUploaded = null): Response
{
if ($this->isRequestMethodIn($request, [Request::METHOD_POST])) {
return $this->save($request, $config, $fileUploaded);
}

throw new MethodNotAllowedHttpException([
Request::METHOD_POST,
]);
}

/**
* @param \Illuminate\Http\Request $request
* @param \CodingSocks\ChunkUploader\StorageConfig $config
* @param \Closure|null $fileUploaded
*
* @return mixed
*/
private function save(Request $request, StorageConfig $config, ?Closure $fileUploaded)
{
$file = $request->file('file');

$this->validateUploadedFile($file);

$this->validateChunkRequest($request);

return $this->saveChunk($file, $request, $config, $fileUploaded);
}

/**
* @param \Illuminate\Http\Request $request
*/
private function validateChunkRequest(Request $request): void
{
$request->validate([
'name' => 'required',
'chunk' => 'required|integer',
'chunks' => 'required|integer',
]);
}

/**
* @param \Illuminate\Http\UploadedFile $file
* @param \Illuminate\Http\Request $request
* @param \CodingSocks\ChunkUploader\StorageConfig $config
* @param \Closure|null $fileUploaded
*
* @return \Symfony\Component\HttpFoundation\Response
*/
private function saveChunk(UploadedFile $file, Request $request, StorageConfig $config, Closure $fileUploaded = null): Response
{
try {
$range = new PluploadRange($request);
} catch (InvalidArgumentException $e) {
throw new BadRequestHttpException($e->getMessage(), $e);
}

$uuid = $this->identifier->generateFileIdentifier($range->getTotal(), $file->getClientOriginalName());

$chunks = $this->storeChunk($config, $range, $file, $uuid);

if (!$range->isLast()) {
return new PercentageJsonResponse($range->getPercentage());
}

$targetFilename = $file->hashName();

$path = $this->mergeChunks($config, $chunks, $targetFilename);

if ($config->sweep()) {
$this->deleteChunkDirectory($config, $uuid);
}

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

return new PercentageJsonResponse($range->getPercentage());
}
}
101 changes: 101 additions & 0 deletions src/Range/PluploadRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace CodingSocks\ChunkUploader\Range;

use Illuminate\Http\Request;
use InvalidArgumentException;

class PluploadRange implements Range
{
private static $CHUNK_NUMBER_PARAMETER_NAME = 'chunk';
private static $TOTAL_CHUNK_NUMBER_PARAMETER_NAME = 'chunks';

/**
* @var float
*/
protected $current;

/**
* @var float
*/
protected $total;

/**
* NgFileUploadRange constructor.
*
* @param \Illuminate\Http\Request|\Symfony\Component\HttpFoundation\ParameterBag $request
*/
public function __construct($request)
{
if ($request instanceof Request) {
$request = $request->request;
}

$this->current = (float) $request->get(self::$CHUNK_NUMBER_PARAMETER_NAME);
$this->total = (float) $request->get(self::$TOTAL_CHUNK_NUMBER_PARAMETER_NAME);

if ($this->current < 0) {
throw new InvalidArgumentException(
sprintf('`%s` must be greater than or equal to zero', self::$CHUNK_NUMBER_PARAMETER_NAME)
);
}
if ($this->total < 1) {
throw new InvalidArgumentException(
sprintf('`%s` must be greater than zero', self::$TOTAL_CHUNK_NUMBER_PARAMETER_NAME)
);
}
if ($this->current >= $this->total) {
throw new InvalidArgumentException(
sprintf('`%s` must be less than `%s`', self::$CHUNK_NUMBER_PARAMETER_NAME, self::$TOTAL_CHUNK_NUMBER_PARAMETER_NAME)
);
}
}

/**
* {@inheritDoc}
*/
public function getStart(): float
{
return $this->current;
}

/**
* {@inheritDoc}
*/
public function getEnd(): float
{
return $this->current + 1;
}

/**
* {@inheritDoc}
*/
public function getTotal(): float
{
return $this->total;
}

/**
* {@inheritDoc}
*/
public function isFirst(): bool
{
return $this->current === 0.0;
}

/**
* {@inheritDoc}
*/
public function isLast(): bool
{
return $this->current >= ($this->total - 1);
}

/**
* @return float
*/
public function getPercentage(): float
{
return floor(($this->current + 1) / $this->total * 100);
}
}
9 changes: 9 additions & 0 deletions src/UploadManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use CodingSocks\ChunkUploader\Driver\DropzoneUploadDriver;
use CodingSocks\ChunkUploader\Driver\FlowJsUploadDriver;
use CodingSocks\ChunkUploader\Driver\MonolithUploadDriver;
use CodingSocks\ChunkUploader\Driver\PluploadUploadDriver;
use CodingSocks\ChunkUploader\Driver\ResumableJsUploadDriver;
use CodingSocks\ChunkUploader\Driver\SimpleUploaderJsUploadDriver;
use Illuminate\Support\Manager;
Expand Down Expand Up @@ -35,6 +36,14 @@ public function createFlowJsDriver()
return new FlowJsUploadDriver($this->app['config']['chunk-uploader.resumable-js']);
}

public function createPluploadDriver()
{
/** @var \Illuminate\Support\Manager $identityManager */
$identityManager = $this->app['chunk-uploader.identity-manager'];

return new PluploadUploadDriver($identityManager->driver());
}

public function createResumableJsDriver()
{
return new ResumableJsUploadDriver($this->app['config']['chunk-uploader.resumable-js']);
Expand Down
Loading