flowjs-server.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <?php
  2. /* Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file htdocs/core/ajax/flowjs-server.php
  19. * \brief File to upload very large file, higher than PHP limit. Using flowjs library.
  20. */
  21. if (!defined('NOTOKENRENEWAL')) {
  22. define('NOTOKENRENEWAL', '1'); // Disables token renewal
  23. }
  24. if (!defined('NOREQUIREMENU')) {
  25. define('NOREQUIREMENU', '1');
  26. }
  27. if (!defined('NOREQUIREHTML')) {
  28. define('NOREQUIREHTML', '1');
  29. }
  30. if (!defined('NOREQUIREAJAX')) {
  31. define('NOREQUIREAJAX', '1');
  32. }
  33. if (!defined('NOREQUIRESOC')) {
  34. define('NOREQUIRESOC', '1');
  35. }
  36. // If there is no need to load and show top and left menu
  37. if (!defined("NOLOGIN")) {
  38. define("NOLOGIN", '1');
  39. }
  40. //if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); // Required to know date format for dol_print_date
  41. // Load Dolibarr environment
  42. require '../../main.inc.php';
  43. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  44. $action = GETPOST('action', 'aZ09');
  45. $module = GETPOST('module', 'aZ09arobase');
  46. $flowFilename = GETPOST('flowFilename', 'alpha');
  47. $flowIdentifier = GETPOST('flowIdentifier', 'alpha');
  48. $flowChunkNumber = GETPOST('flowChunkNumber', 'alpha');
  49. $flowChunkSize = GETPOST('flowChunkSize', 'alpha');
  50. $flowTotalSize = GETPOST('flowTotalSize', 'alpha');
  51. $result = restrictedArea($user, $module, 0, '', 0, 'fk_soc', 'rowid', 0, 1); // Call with mode return
  52. if ($action != 'upload') {
  53. httponly_accessforbidden("Param action must be 'upload'");
  54. }
  55. if (!empty($conf->$module->dir_temp)) {
  56. $upload_dir = $conf->$module->dir_temp;
  57. } else {
  58. httponly_accessforbidden("Param module does not has a dir_temp directory. Module does not exists or is not activated.");
  59. }
  60. /*
  61. * Action
  62. */
  63. top_httphead();
  64. dol_syslog(join(',', $_GET));
  65. $result = false;
  66. if (!empty($upload_dir)) {
  67. $temp_dir = $upload_dir.'/'.$flowIdentifier;
  68. } else {
  69. $temp_dir = DOL_DATA_ROOT.'/'.$module.'/temp/'.$flowIdentifier;
  70. $upload_dir = DOL_DATA_ROOT.'/'.$module.'/temp/';
  71. }
  72. if ($module != "test" && !isModEnabled($module)) {
  73. echo json_encode("The module ".$module." is not enabled");
  74. header("HTTP/1.0 400");
  75. die();
  76. }
  77. if ($_SERVER['REQUEST_METHOD'] === 'GET') {
  78. $chunk_file = $temp_dir.'/'.$flowFilename.'.part'.$flowChunkNumber;
  79. if (file_exists($chunk_file)) {
  80. header("HTTP/1.0 200 Ok");
  81. } else {
  82. header("HTTP/1.0 404 Not Found");
  83. }
  84. } else {
  85. // loop through files and move the chunks to a temporarily created directory
  86. if (file_exists($upload_dir.'/'.$flowFilename)) {
  87. echo json_encode('File '.$flowIdentifier.' was already uploaded');
  88. header("HTTP/1.0 200 Ok");
  89. die();
  90. } elseif (!empty($_FILES)) {
  91. foreach ($_FILES as $file) {
  92. // check the error status
  93. if ($file['error'] != 0) {
  94. dol_syslog('error '.$file['error'].' in file '.$flowFilename);
  95. continue;
  96. }
  97. // init the destination file (format <filename.ext>.part<#chunk>
  98. // the file is stored in a temporary directory
  99. $dest_file = $temp_dir.'/'.$flowFilename.'.part'.$flowChunkNumber;
  100. // create the temporary directory
  101. if (!dol_is_dir($temp_dir)) {
  102. dol_mkdir($temp_dir);
  103. }
  104. // move the temporary file
  105. if (!dol_move_uploaded_file($file['tmp_name'], $dest_file, 0)) {
  106. dol_syslog('Error saving (move_uploaded_file) chunk '.$flowChunkNumber.' for file '.$flowFilename);
  107. } else {
  108. // check if all the parts present, and create the final destination file
  109. $result = createFileFromChunks($temp_dir, $upload_dir, $flowFilename, $flowChunkSize, $flowTotalSize);
  110. }
  111. }
  112. }
  113. }
  114. if ($result) {
  115. echo json_encode('File '.$flowIdentifier.' uploaded');
  116. } else {
  117. echo json_encode('Error while uploading file '.$flowIdentifier);
  118. }
  119. /**
  120. * Check if all the parts exist, and gather all the parts of the file together.
  121. *
  122. * @param string $temp_dir the temporary directory holding all the parts of the file
  123. * @param string $upload_dir the temporary directory to create file
  124. * @param string $fileName the original file name
  125. * @param string $chunkSize each chunk size (in bytes)
  126. * @param string $totalSize original file size (in bytes)
  127. * @return bool true if Ok false else
  128. */
  129. function createFileFromChunks($temp_dir, $upload_dir, $fileName, $chunkSize, $totalSize)
  130. {
  131. dol_syslog(__FUNCTION__, LOG_DEBUG);
  132. // count all the parts of this file
  133. $total_files = 0;
  134. $files = dol_dir_list($temp_dir, 'files');
  135. foreach ($files as $file) {
  136. if (stripos($file["name"], $fileName) !== false) {
  137. $total_files++;
  138. }
  139. }
  140. // check that all the parts are present
  141. // the size of the last part is between chunkSize and 2*$chunkSize
  142. if ($total_files * $chunkSize >= ($totalSize - $chunkSize + 1)) {
  143. // create the final destination file
  144. if (($fp = fopen($upload_dir.'/'.$fileName, 'w')) !== false) {
  145. for ($i=1; $i<=$total_files; $i++) {
  146. fwrite($fp, file_get_contents($temp_dir.'/'.$fileName.'.part'.$i));
  147. dol_syslog('writing chunk '.$i);
  148. }
  149. fclose($fp);
  150. } else {
  151. dol_syslog('cannot create the destination file');
  152. return false;
  153. }
  154. // rename the temporary directory (to avoid access from other
  155. // concurrent chunks uploads)
  156. @rename($temp_dir, $temp_dir.'_UNUSED');
  157. }
  158. return true;
  159. }