Skip to content

Support for resumable uploads #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
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
16 changes: 8 additions & 8 deletions readme.md → README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ Setup is composed in 3 steps:
2. Setting route for the controller. [How to](https://github.com/pionl/laravel-chunk-upload/wiki/routing)
2. Choose your front-end provider below (we support multiple providers in single controller)

| Library | Wiki | single & chunk upload | simultaneous uploads | In [example project](https://github.com/pionl/laravel-chunk-upload-example) | Author |
|---- |----|----|----| ---- | ---- |
| [resumable.js](https://github.com/23/resumable.js) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/jquery-file-upload) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [DropZone](https://gitlab.com/meno/dropzone/) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/dropzone) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [jQuery-File-Upload](https://github.com/blueimp/jQuery-File-Upload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/blueimp-file-upload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [Plupload](https://github.com/moxiecode/plupload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/plupload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | [@pionl](https://github.com/pionl) |
| [simple uploader](https://github.com/simple-uploader) | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | [@dyktek](https://github.com/dyktek) |
| [ng-file-upload](https://github.com/danialfarid/ng-file-upload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/ng-file-upload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | [@L3o-pold](https://github.com/L3o-pold) |
| Library | Wiki | single & chunk upload | simultaneous uploads | resume uploads | In [example project](https://github.com/pionl/laravel-chunk-upload-example) | Author |
| ---- | ---- | ---- | ---- | ---- | ---- |---- |
| [resumable.js](https://github.com/23/resumable.js) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/jquery-file-upload) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [DropZone](https://gitlab.com/meno/dropzone/) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/dropzone) | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [jQuery-File-Upload](https://github.com/blueimp/jQuery-File-Upload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/blueimp-file-upload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: | [@pionl](https://github.com/pionl) |
| [Plupload](https://github.com/moxiecode/plupload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/plupload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: | [@pionl](https://github.com/pionl) |
| [simple uploader](https://github.com/simple-uploader) | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | [@dyktek](https://github.com/dyktek) |
| [ng-file-upload](https://github.com/danialfarid/ng-file-upload) | [Wiki](https://github.com/pionl/laravel-chunk-upload/wiki/ng-file-upload) | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | [@L3o-pold](https://github.com/L3o-pold) |

**Simultaneous uploads:** The library must send last chunk as last, otherwise the merging will not work correctly.

Expand Down
55 changes: 55 additions & 0 deletions src/Handler/AbstractCheckHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
*
*/

namespace Pion\Laravel\ChunkUpload\Handler;

use Illuminate\Http\Request;
use Pion\Laravel\ChunkUpload\Config\AbstractConfig;
use Pion\Laravel\ChunkUpload\Storage\ChunkStorage;

abstract class AbstractCheckHandler extends AbstractHandler
{
/**
* AbstractReceiver constructor.
*
* @param Request $request
* @param AbstractConfig $config
*/
public function __construct(Request $request, $config)
{
parent::__construct($request, $this->getFilenameFromRequest($request), $config);
}

/**
* Returns the full file path to the requested file
*
* @param ChunkStorage $chunkStorage
* @return string
*/
public function getFullFilePath($chunkStorage)
{
$path[] = $chunkStorage->getDiskPathPrefix();
$path[] = $chunkStorage->directory();
$path[] = $this->getChunkFileName();

return implode('', $path);
}

/**
* Returns the filename from the request
*
* @param \Illuminate\Http\Request $request
* @return string
*/
abstract public function getFilenameFromRequest(Request $request);

/**
* Checks if the target file or chunk is already uploaded
*
* @param ChunkStorage $chunkStorage
* @return false|array
*/
abstract public function check($chunkStorage);
}
50 changes: 6 additions & 44 deletions src/Handler/AbstractHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Pion\Laravel\ChunkUpload\Config\AbstractConfig;
use Pion\Laravel\ChunkUpload\Save\AbstractSave;
use Pion\Laravel\ChunkUpload\Storage\ChunkStorage;
use Session;

Expand All @@ -20,9 +19,9 @@ abstract class AbstractHandler
protected $request;

/**
* @var UploadedFile
* @var string
*/
protected $file;
protected $filename;

/**
* @var AbstractConfig
Expand All @@ -33,13 +32,13 @@ abstract class AbstractHandler
* AbstractReceiver constructor.
*
* @param Request $request
* @param UploadedFile $file
* @param string $filename
* @param AbstractConfig $config
*/
public function __construct(Request $request, $file, $config)
public function __construct(Request $request, $filename, $config)
{
$this->request = $request;
$this->file = $file;
$this->filename = $filename;
$this->config = $config;
}

Expand Down Expand Up @@ -92,7 +91,7 @@ public function createChunkFileName($additionalName = null, $currentChunkIndex =
{
// prepare basic name structure
$array = [
$this->file->getClientOriginalName(),
$this->filename,
];

// ensure that the chunk name is for unique for the client session
Expand Down Expand Up @@ -135,47 +134,10 @@ public function createChunkFileName($additionalName = null, $currentChunkIndex =
return implode('.', $namesSeparatedByDot);
}

/**
* Creates save instance and starts saving the uploaded file.
*
* @param ChunkStorage $chunkStorage the chunk storage
*
* @return AbstractSave
*/
abstract public function startSaving($chunkStorage);

/**
* Returns the chunk file name for a storing the tmp file.
*
* @return string
*/
abstract public function getChunkFileName();

/**
* Checks if the request has first chunk.
*
* @return bool
*/
abstract public function isFirstChunk();

/**
* Checks if the current request has the last chunk.
*
* @return bool
*/
abstract public function isLastChunk();

/**
* Checks if the current request is chunked upload.
*
* @return bool
*/
abstract public function isChunkedUpload();

/**
* Returns the percentage of the upload file.
*
* @return int
*/
abstract public function getPercentageDone();
}
74 changes: 74 additions & 0 deletions src/Handler/AbstractUploadHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Pion\Laravel\ChunkUpload\Handler;

use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Pion\Laravel\ChunkUpload\Config\AbstractConfig;
use Pion\Laravel\ChunkUpload\Save\AbstractSave;
use Pion\Laravel\ChunkUpload\Storage\ChunkStorage;

/**
* The handler that will detect if we can continue the chunked upload
*
* @package Pion\Laravel\ChunkUpload\Handler
*/
abstract class AbstractUploadHandler extends AbstractHandler
{
/**
* @var UploadedFile
*/
protected $file;

/**
* AbstractReceiver constructor.
*
* @param Request $request
* @param UploadedFile $file
* @param AbstractConfig $config
*/
public function __construct(Request $request, $file, $config)
{
$filename = (null === $file) ? null : $file->getClientOriginalName();
parent::__construct($request, $filename, $config);

$this->file = $file;
}

/**
* Creates save instance and starts saving the uploaded file
*
* @param ChunkStorage $chunkStorage the chunk storage
*
* @return AbstractSave
*/
abstract public function startSaving($chunkStorage);

/**
* Checks if the request has first chunk
*
* @return bool
*/
abstract public function isFirstChunk();

/**
* Checks if the current request has the last chunk
*
* @return bool
*/
abstract public function isLastChunk();

/**
* Checks if the current request is chunked upload
*
* @return bool
*/
abstract public function isChunkedUpload();

/**
* Returns the percentage of the upload file
*
* @return int
*/
abstract public function getPercentageDone();
}
17 changes: 17 additions & 0 deletions src/Handler/CheckHandlerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
namespace Pion\Laravel\ChunkUpload\Handler;

class CheckHandlerFactory extends HandlerFactory
{
/**
* Decides if the implementation of the factory supports a specific handler or not.
*
* @param $handlerClass
*
* @return bool
*/
protected static function filterHandler($handlerClass)
{
return is_subclass_of($handlerClass, AbstractCheckHandler::class);
}
}
107 changes: 107 additions & 0 deletions src/Handler/ChunkCheckHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
*
*/

namespace Pion\Laravel\ChunkUpload\Handler;

use Illuminate\Http\Request;
use Pion\Laravel\ChunkUpload\Config\AbstractConfig;
use Pion\Laravel\ChunkUpload\Storage\ChunkStorage;

/**
* Class ResumableJSCheckHandler
*
* Check handler that tells if the requested chunk is already uploaded or not
*
* Works with:
* - simple-uploader: https://github.com/simple-uploader/Uploader
*
* @package Pion\Laravel\ChunkUpload\Handler
*/
class ChunkCheckHandler extends AbstractCheckHandler
{
const CHUNK_UUID_INDEX = 'identifier';

const CHUNK_NUMBER_INDEX = 'chunkNumber';

const FILENAME_INDEX = 'filename';

/**
* The current chunk progress
*
* @var int
*/
protected $currentChunk = 0;

/**
* The Resumable file uuid for unique chunk upload session.
*
* @var string|null
*/
protected $fileUuid = null;

/**
* AbstractReceiver constructor.
*
* @param Request $request
* @param AbstractConfig $config
*/
public function __construct(Request $request, $config)
{
parent::__construct($request, $config);

$this->currentChunk = intval($request->get(static::CHUNK_NUMBER_INDEX)) + 1;
$this->fileUuid = $request->get(static::CHUNK_UUID_INDEX);
}

/**
* Returns the filename from the request
*
* @param \Illuminate\Http\Request $request
* @return string
*/
public function getFilenameFromRequest(Request $request)
{
return $request->get(static::FILENAME_INDEX);
}

/**
* Returns the chunk file name for a storing the tmp file
*
* @return string
*/
public function getChunkFileName()
{
return $this->createChunkFileName($this->fileUuid, $this->currentChunk);
}

/**
* Checks if the current abstract handler can be used via HandlerFactory
*
* @param Request $request
*
* @return bool
*/
public static function canBeUsedForRequest(Request $request)
{
return $request->has(static::CHUNK_UUID_INDEX, static::CHUNK_NUMBER_INDEX, static::FILENAME_INDEX);
}

/**
* Checks if the target file or chunk is already uploaded
*
* @param ChunkStorage $chunkStorage
* @return false|array
*/
public function check($chunkStorage)
{
$fullFilePath = $this->getFullFilePath($chunkStorage);

if(!\File::exists($fullFilePath)) {
return false;
}

return [];
}
}
2 changes: 1 addition & 1 deletion src/Handler/ChunksInRequestUploadHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* Works with:
* - PUpload: https://github.com/moxiecode/plupload/
*/
class ChunksInRequestUploadHandler extends AbstractHandler
class ChunksInRequestUploadHandler extends AbstractUploadHandler
{
/**
* Key for number of sending chunk.
Expand Down
2 changes: 1 addition & 1 deletion src/Handler/ContentRangeUploadHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* - blueimp-file-upload - partial support (simple chunked and single upload)
* https://github.com/blueimp/jQuery-File-Upload
*/
class ContentRangeUploadHandler extends AbstractHandler
class ContentRangeUploadHandler extends AbstractUploadHandler
{
/**
* The index for the header.
Expand Down
Loading