Resumable.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. namespace Dilab;
  3. use Cake\Filesystem\File;
  4. use Cake\Filesystem\Folder;
  5. use Dilab\Network\Request;
  6. use Dilab\Network\Response;
  7. class Resumable
  8. {
  9. public $tempFolder = 'tmp';
  10. public $uploadFolder = 'test/files/uploads';
  11. // for testing
  12. public $deleteTmpFolder = true;
  13. protected $request;
  14. protected $response;
  15. protected $params;
  16. protected $chunkFile;
  17. public function __construct(Request $request, Response $response)
  18. {
  19. $this->request = $request;
  20. $this->response = $response;
  21. }
  22. public function process()
  23. {
  24. if (!empty($this->resumableParams())) {
  25. if (!empty($this->request->file())) {
  26. $this->handleChunk();
  27. } else {
  28. $this->handleTestChunk();
  29. }
  30. }
  31. }
  32. public function handleTestChunk()
  33. {
  34. $identifier = $this->_resumableParam('identifier');
  35. $filename = $this->_resumableParam('filename');
  36. $chunkNumber = $this->_resumableParam('chunkNumber');
  37. if (!$this->isChunkUploaded($identifier,$filename,$chunkNumber)) {
  38. return $this->response->header(404);
  39. } else {
  40. return $this->response->header(200);
  41. }
  42. }
  43. public function handleChunk()
  44. {
  45. $file = $this->request->file();
  46. $identifier = $this->_resumableParam('identifier');
  47. $filename = $this->_resumableParam('filename');
  48. $chunkNumber = $this->_resumableParam('chunkNumber');
  49. $chunkSize = $this->_resumableParam('chunkSize');
  50. $totalSize = $this->_resumableParam('totalSize');
  51. if (!$this->isChunkUploaded($identifier,$filename,$chunkNumber)) {
  52. $chunkFile = $this->tmpChunkDir($identifier).DIRECTORY_SEPARATOR.$this->tmpChunkFilename($filename, $chunkNumber);
  53. $this->moveUploadedFile($file['tmp_name'], $chunkFile);
  54. }
  55. if ($this->isFileUploadComplete($filename,$identifier,$chunkSize, $totalSize)) {
  56. $tmpFolder = new Folder($this->tmpChunkDir($identifier));
  57. $chunkFiles = $tmpFolder->read(true,true,true)[1];
  58. $this->createFileFromChunks($chunkFiles, $this->uploadFolder.DIRECTORY_SEPARATOR.$filename);
  59. if ($this->deleteTmpFolder) {
  60. $tmpFolder->delete();
  61. }
  62. }
  63. return $this->response->header(200);
  64. }
  65. private function _resumableParam($shortName) {
  66. $resumableParams = $this->resumableParams();
  67. if (!isset($resumableParams['resumable'.ucfirst($shortName)])) {
  68. return null;
  69. }
  70. return $resumableParams['resumable'.ucfirst($shortName)];
  71. }
  72. public function resumableParams()
  73. {
  74. if ($this->request->is('get')) {
  75. return $this->request->data('get');
  76. }
  77. if ($this->request->is('post')) {
  78. return $this->request->data('post');
  79. }
  80. }
  81. public function isFileUploadComplete($filename, $identifier, $chunkSize, $totalSize)
  82. {
  83. if ($chunkSize <= 0) {
  84. return false;
  85. }
  86. $numOfChunks = intval($totalSize / $chunkSize) + ($totalSize % $chunkSize == 0 ? 0 : 1);
  87. for ($i = 1; $i < $numOfChunks; $i++) {
  88. if (!$this->isChunkUploaded($identifier, $filename, $i)) {
  89. return false;
  90. }
  91. }
  92. return true;
  93. }
  94. public function isChunkUploaded($identifier, $filename, $chunkNumber)
  95. {
  96. $file = new File($this->tmpChunkDir($identifier) . DIRECTORY_SEPARATOR . $this->tmpChunkFilename($filename, $chunkNumber));
  97. return $file->exists();
  98. }
  99. public function tmpChunkDir($identifier)
  100. {
  101. $tmpChunkDir = $this->tempFolder . DIRECTORY_SEPARATOR . $identifier;
  102. if (!file_exists($tmpChunkDir)) {
  103. mkdir($tmpChunkDir);
  104. }
  105. return $tmpChunkDir;
  106. }
  107. public function tmpChunkFilename($filename, $chunkNumber)
  108. {
  109. return $filename . '.part' . $chunkNumber;
  110. }
  111. public function createFileFromChunks($chunkFiles, $destFile)
  112. {
  113. $destFile = new File($destFile, true);
  114. foreach ($chunkFiles as $chunkFile) {
  115. $file = new File($chunkFile);
  116. $destFile->append($file->read());
  117. }
  118. return $destFile->exists();
  119. }
  120. public function moveUploadedFile($file, $destFile)
  121. {
  122. $file = new File($file);
  123. if ($file->exists()) {
  124. return $file->copy($destFile);
  125. }
  126. return false;
  127. }
  128. public function setRequest($request)
  129. {
  130. $this->request = $request;
  131. }
  132. public function setResponse($response)
  133. {
  134. $this->response = $response;
  135. }
  136. }