Skip to content

Commit

Permalink
Merge pull request #36 from abilogos/improved
Browse files Browse the repository at this point in the history
Improved
  • Loading branch information
dilab authored Dec 15, 2023
2 parents eeb8298 + 72f438c commit 27fa0c5
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 77 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
vendor/*
composer.lock
.idea
.idea
.phpunit.cache/*
.phpunit.*.cache
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
}
],
"require": {
"php": ">=5.3.0",
"php": ">=8.1.0",
"cakephp/filesystem": "^3.0",
"monolog/monolog": "^1.17"
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "~10.0"
},
"license": "MIT",
"autoload": {
Expand Down
24 changes: 7 additions & 17 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="true"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./test/</directory>
</testsuite>
</testsuites>
</phpunit>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="./vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./test/</directory>
</testsuite>
</testsuites>
</phpunit>
17 changes: 14 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# PHP backend for resumable.js


## Installation

To install, use composer:
Expand All @@ -21,11 +20,23 @@ use Dilab\Resumable;
$request = new SimpleRequest();
$response = new SimpleResponse();
// optional instanceId to seperate uploads from diffrent users like if two users want to upload untitled.jpg there would be no conflict anymore
$instanceId = session_id();
$resumable = new Resumable($request, $response);
$resumable = new Resumable($request, $response, $instanceId);
$resumable->tempFolder = 'tmps';
$resumable->uploadFolder = 'uploads';
$resumable->process();
$status = $resumable->process();
return match ($status){
200 => ['message' => 'OK'], // Uploading of chunk is complete.
201 => [
'message' => 'File uploaded',
'file' => $_REQUEST['resumableFilename']
],// Uploading of whole file is complete.
204 => ['message' => 'Chunk not found'],
default => ['message' => 'An error occurred'] //status => 404
};
```

Expand Down
12 changes: 7 additions & 5 deletions src/Network/SimpleResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ class SimpleResponse implements Response
*/
public function header($statusCode)
{
if (200==$statusCode) {
return header($_SERVER["SERVER_PROTOCOL"]." 200 Ok");
} else if (404==$statusCode) {
return header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
if($statusCode >= 500) {
$statusCode = 204;
}
return header($_SERVER["SERVER_PROTOCOL"]." 204 No Content");
if (!in_array($statusCode, [200,201,204,404])) {
$statusCode = 404;
}
http_response_code($statusCode);
return $statusCode;
}

}
77 changes: 61 additions & 16 deletions src/Resumable.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Resumable

protected $response;

protected $instanceId;

protected $params;

protected $chunkFile;
Expand All @@ -45,15 +47,17 @@ class Resumable
'filename' => 'filename',
'chunkNumber' => 'chunkNumber',
'chunkSize' => 'chunkSize',
'totalSize' => 'totalSize'
'totalSize' => 'totalSize',
'totalChunks' => 'totalChunks'
];

const WITHOUT_EXTENSION = true;

public function __construct(Request $request, Response $response)
public function __construct(Request $request, Response $response, string|null $instanceId = null)
{
$this->request = $request;
$this->response = $response;
$this->instanceId = $instanceId;

$this->log = new Logger('debug');
$this->log->pushHandler(new StreamHandler('debug.log', Logger::DEBUG));
Expand Down Expand Up @@ -81,9 +85,9 @@ public function process()
{
if (!empty($this->resumableParams())) {
if (!empty($this->request->file())) {
$this->handleChunk();
return $this->handleChunk();
} else {
$this->handleTestChunk();
return $this->handleTestChunk();
}
}
}
Expand Down Expand Up @@ -174,10 +178,17 @@ public function handleTestChunk()
$identifier = $this->resumableParam($this->resumableOption['identifier']);
$filename = $this->resumableParam($this->resumableOption['filename']);
$chunkNumber = $this->resumableParam($this->resumableOption['chunkNumber']);
$chunkSize = $this->resumableParam($this->resumableOption['chunkSize']);
$totalChunks = $this->resumableParam($this->resumableOption['totalChunks']);

if (!$this->isChunkUploaded($identifier, $filename, $chunkNumber)) {
return $this->response->header(204);
} else {
if ($this->isFileUploadComplete($filename, $identifier, $totalChunks)) {
$this->isUploadComplete = true;
$this->createFileAndDeleteTmp($identifier, $filename);
return $this->response->header(201);
}
return $this->response->header(200);
}

Expand All @@ -190,16 +201,17 @@ public function handleChunk()
$filename = $this->resumableParam($this->resumableOption['filename']);
$chunkNumber = $this->resumableParam($this->resumableOption['chunkNumber']);
$chunkSize = $this->resumableParam($this->resumableOption['chunkSize']);
$totalSize = $this->resumableParam($this->resumableOption['totalSize']);
$totalChunks = $this->resumableParam($this->resumableOption['totalChunks']);

if (!$this->isChunkUploaded($identifier, $filename, $chunkNumber)) {
$chunkFile = $this->tmpChunkDir($identifier) . DIRECTORY_SEPARATOR . $this->tmpChunkFilename($filename, $chunkNumber);
$this->moveUploadedFile($file['tmp_name'], $chunkFile);
}

if ($this->isFileUploadComplete($filename, $identifier, $chunkSize, $totalSize)) {
if ($this->isFileUploadComplete($filename, $identifier, $totalChunks)) {
$this->isUploadComplete = true;
$this->createFileAndDeleteTmp($identifier, $filename);
return $this->response->header(201);
}

return $this->response->header(200);
Expand All @@ -221,7 +233,12 @@ private function createFileAndDeleteTmp($identifier, $filename)
}

// replace filename reference by the final file
$this->filepath = $this->uploadFolder . DIRECTORY_SEPARATOR . $finalFilename;
$this->filepath = $this->uploadFolder . DIRECTORY_SEPARATOR;
if (!empty($this->instanceId)) {
$this->filepath .= $this->instanceId . DIRECTORY_SEPARATOR;
}
$this->filepath .= $finalFilename;

$this->extension = $this->findExtension($this->filepath);

if ($this->createFileFromChunks($chunkFiles, $this->filepath) && $this->deleteTmpFolder) {
Expand Down Expand Up @@ -249,13 +266,9 @@ public function resumableParams()
}
}

public function isFileUploadComplete($filename, $identifier, $chunkSize, $totalSize)
public function isFileUploadComplete($filename, $identifier, $totalChunks)
{
if ($chunkSize <= 0) {
return false;
}
$numOfChunks = intval($totalSize / $chunkSize) + ($totalSize % $chunkSize == 0 ? 0 : 1);
for ($i = 1; $i < $numOfChunks; $i++) {
for ($i = 1; $i <= $totalChunks; $i++) {
if (!$this->isChunkUploaded($identifier, $filename, $i)) {
return false;
}
Expand All @@ -271,13 +284,38 @@ public function isChunkUploaded($identifier, $filename, $chunkNumber)

public function tmpChunkDir($identifier)
{
$tmpChunkDir = $this->tempFolder . DIRECTORY_SEPARATOR . $identifier;
if (!file_exists($tmpChunkDir)) {
mkdir($tmpChunkDir);
$tmpChunkDir = $this->tempFolder. DIRECTORY_SEPARATOR;
if (!empty($this->instanceId)){
$tmpChunkDir .= $this->instanceId . DIRECTORY_SEPARATOR;
}
$tmpChunkDir .= $identifier;
$this->ensureDirExists($tmpChunkDir);
return $tmpChunkDir;
}

/**
* make directory if it doesn't exists (Immune against the race condition)
*
*
* since the resuamble is usually used with simultaneously uploads,
* this sometimes resulted in directory creation btween the *is_dir* check
* and *mkdir* then following race condition.
* in this setup it will shut down the mkdir error
* then try to check if directory is created after that
*
* @param string $path the directoryPath to ensure
* @return void
* @throws \Exception
*/
private function ensureDirExists($path)
{
umask(0);
if ( is_dir($path) || @mkdir($path, 0775, true) || is_dir($path)) {
return;
}
throw new \Exception("could not mkdir $path");
}

public function tmpChunkFilename($filename, $chunkNumber)
{
return $filename . '.' . str_pad($chunkNumber, 4, 0, STR_PAD_LEFT);
Expand All @@ -299,6 +337,10 @@ public function createFileFromChunks($chunkFiles, $destFile)

natsort($chunkFiles);

if (!empty($this->instanceId)) {
$this->ensureDirExists(dirname($destFile));
}

$handle = $this->getExclusiveFileHandle($destFile);
if (!$handle) {
return false;
Expand All @@ -319,6 +361,9 @@ public function createFileFromChunks($chunkFiles, $destFile)

public function moveUploadedFile($file, $destFile)
{
//workaround cakephp error regarding: TMP not defined
define("TMP",sys_get_temp_dir());

$file = new File($file);
if ($file->exists()) {
return $file->copy($destFile);
Expand Down
6 changes: 3 additions & 3 deletions test/src/Network/SimpleRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
* @package Dilab\Network
* @property $request Request
*/
class SimpleRequestTest extends \PHPUnit_Framework_TestCase
class SimpleRequestTest extends \PHPUnit\Framework\TestCase
{
protected function setUp()
protected function setUp() : void
{
$this->request = new SimpleRequest();
}

public function tearDown()
public function tearDown() : void
{
unset($this->request);
parent::tearDown();
Expand Down
20 changes: 10 additions & 10 deletions test/src/Network/SimpleResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@
* @package Dilab\Network
* @property $response Response
*/
class SimpleResponseTest extends \PHPUnit_Framework_TestCase
class SimpleResponseTest extends \PHPUnit\Framework\TestCase
{
protected function setUp()
protected function setUp() : void
{
$this->response = new SimpleResponse();
}

public function tearDown()
public function tearDown() : void
{
unset($this->response);
parent::tearDown();
}


public function headerProvider()
public static function headerProvider()
{
return array(
array(404,404),
array(204,204),
array(200,200),
array(500,204),
);
return [
[404,404],
[204,204],
[200,200],
[500,204],
];
}

/**
Expand Down
Loading

0 comments on commit 27fa0c5

Please sign in to comment.