FileStorage.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. // @requires $_SERVER['SERVER_NAME']
  3. Lib::loadClass('RouteBase');
  4. Lib::loadClass('Schema_TableFactory');
  5. Lib::loadClass('Response');
  6. Lib::loadClass('UI');
  7. Lib::loadClass('OBJ');
  8. /*
  9. # FileStorage:
  10. - [x] create CRM_FILES - sql at the end of file
  11. - [x] only meta fields, perms? - no perms in relation based on record which is connected
  12. - [x] add file to storage - API POST/PUT action
  13. - [ ] base storage folder - from config - default /Library/Server/Web/Data/p5-pliki, chmod - add to instalator, upgrade
  14. - [x] subfolders like postfix, but [0-9],[A-Z] - substr(strrev(base_convert($id, 10, 10 + 26)), 0, 1) - 36 folderów
  15. - [x] file connected to user - A_RECORD_CREATE_AUTHOR
  16. - [ ] connect file to record - {tbl_name}__REF__FILES
  17. # upload API: `index.php?_route=FileStorage&_task=upload&name={file_name}`
  18. */
  19. class Route_FileStorage extends RouteBase {
  20. public function handleAuth() {
  21. if (!User::logged()) {
  22. User::authByRequest();
  23. }
  24. }
  25. public function defaultAction() {
  26. UI::gora();
  27. UI::menu();
  28. try {
  29. echo '...';
  30. } catch (Exception $e) {
  31. UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage());
  32. }
  33. UI::dol();
  34. }
  35. public function addFile($content, $name = '') {
  36. $rootFileStoragePath = '/tmp/test-upload-file-storage';// TODO: from config!
  37. $sqlLogin = User::getLogin();
  38. $sqlLabel = DB::getPDO()->quote($sqlLabel, PDO::PARAM_STR);
  39. $sql = "
  40. insert into CRM_FILES (`A_RECORD_CREATE_AUTHOR`,`A_RECORD_CREATE_DATE`,`FILE_LABEL`)
  41. values ('{$sqlLogin}', NOW(), {$sqlLabel})
  42. ";
  43. DBG::_('DBG', '>2', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
  44. DB::getPDO()->exec($sql);
  45. $dbLastId = DB::getPDO()->lastInsertId();
  46. DBG::_('DBG', '>1', "dbLastId", $dbLastId, __CLASS__, __FUNCTION__, __LINE__);
  47. $filePath = $this->generateFilePathHashFromID($dbLastId);
  48. DBG::_('DBG', '>1', "filePath", $filePath, __CLASS__, __FUNCTION__, __LINE__);
  49. $absFilePath = "{$rootFileStoragePath}/{$filePath}";
  50. $dirPath = dirname($absFilePath);
  51. @mkdir($dirPath, $mode = 0777, $recursive = true);
  52. if (!file_exists($dirPath)) throw new Exception("Cannot create path");
  53. @chmod($dirPath, $mode = 0777);
  54. $fp = fopen($absFilePath, 'w');
  55. fwrite($fp, $fileContent);
  56. fclose($fp);
  57. if (!file_exists($dirPath)) throw new Exception("Cannot save file");
  58. }
  59. public function uploadAction() {
  60. try {
  61. $fileContent = Request::getRequestBody();
  62. $sqlLabel = V::get('name', '', $_REQUEST);
  63. $this->addFile($fileContent, $sqlLabel);
  64. echo 'file uploaded';
  65. } catch (Exception $e) {
  66. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  67. }
  68. }
  69. public function uploadFromBinaryTestAction() {
  70. try {
  71. $fileContent = Request::getRequestBody();
  72. $filePath = '/tmp/test-upload-data.txt';
  73. $fp = fopen($filePath, 'w');
  74. fwrite($fp, $fileContent);
  75. fclose($fp);
  76. echo 'file uploaded?';
  77. } catch (Exception $e) {
  78. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  79. }
  80. }
  81. public function downloadTestAction() {
  82. try {
  83. $filePath = '/tmp/test-upload-data.txt';
  84. if (!file_exists($filePath)) throw new Exception("file not exists!");
  85. header('Content-Description: File Transfer');
  86. header('Content-Type: application/octet-stream');
  87. header('Content-Disposition: attachment; filename="'.basename($filePath).'"');
  88. header('Expires: 0');
  89. header('Cache-Control: must-revalidate');
  90. header('Pragma: public');
  91. header('Content-Length: ' . filesize($filePath));
  92. readfile($filePath);
  93. exit;
  94. } catch (Exception $e) {
  95. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  96. }
  97. }
  98. public function uploadFromFormTestAction() {
  99. try {
  100. // $fileContent = Request::getRequestBody();
  101. DBG::_(true, true, '_POST', $_POST, __CLASS__, __FUNCTION__, __LINE__);
  102. // $filePath = '/tmp/test-upload-data.txt';
  103. // $fp = fopen($filePath, 'w');
  104. // fwrite($fp, $fileContent);
  105. // fclose($fp);
  106. // echo 'file uploaded?';
  107. } catch (Exception $e) {
  108. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  109. }
  110. }
  111. public function uploadFormTestAction() {
  112. UI::gora();
  113. UI::menu();
  114. try {
  115. // multiple: <input type="file" id="file_input" multiple="multiple" />
  116. // only images: <input type="file" id="file_input" multiple="multiple" accept="image/*" />
  117. ?>
  118. <div class="container">
  119. <form>
  120. <input type="file" id="file_input" style="display:block; width:100%; height:200px; background-color:silver; text-align:center">
  121. </form>
  122. <button class="btn btn-primary" id="upload_file_as_binary_btn">upload as binary</button>
  123. <button class="btn btn-default" id="upload_file_as_form_btn">upload as form</button>
  124. <a class="btn btn-default" href="index.php?_route=FileStorage&_task=downloadTest" target="_blank">download</a>
  125. </div>
  126. <script>
  127. document.getElementById('file_input').addEventListener('change', function() {
  128. for (var i = 0; i < this.files.length; i++){
  129. var file = this.files[i];
  130. // This code is only for demo ...
  131. console.group("File "+i);
  132. console.log("name : " + file.name);
  133. console.log("size : " + file.size);
  134. console.log("type : " + file.type);
  135. console.log("date : " + file.lastModified);
  136. console.groupEnd();
  137. }
  138. }, false);
  139. function uploadFileAsForm(file) {
  140. var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=uploadFromFormTest"; ?>';
  141. var xhr = new XMLHttpRequest();
  142. var fd = new FormData();
  143. xhr.open("POST", serverUrl, true);
  144. xhr.onreadystatechange = function() {
  145. if (xhr.readyState == 4 && xhr.status == 200) {
  146. // Every thing ok, file uploaded
  147. console.log(xhr.responseText); // handle response.
  148. }
  149. };
  150. fd.append("upload_file", file);
  151. xhr.send(fd);
  152. }
  153. function uploadFileAsBinary(file) {
  154. var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=uploadFromBinaryTest"; ?>';
  155. var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=upload"; ?>';
  156. var _dbg = true;
  157. // .set('Accept', 'application/json')
  158. superagent.post(serverUrl + '&name=' + file.name)
  159. .set('Content-Type', file.type)
  160. .send(file)
  161. .end(function(err, res) {
  162. if(_dbg)console.log('DBG: res:', res, 'res.body:', res.body);
  163. })
  164. return
  165. jQuery.ajax({
  166. type: "POST",
  167. beforeSend: function (request) {
  168. request.setRequestHeader("Content-Type", file.type);
  169. },
  170. url: serverUrl,
  171. data: file,
  172. processData: false,
  173. contentType: false,
  174. success: function (data) {
  175. console.log("File available at: ", data);
  176. },
  177. error: function (data) {
  178. var obj = jQuery.parseJSON(data);
  179. alert(obj.error);
  180. }
  181. })
  182. }
  183. jQuery('#upload_file_as_binary_btn').on('click', function() {
  184. var fileInput = document.getElementById('file_input')
  185. if (!fileInput) return false;// TODO: error msg
  186. if (!fileInput.files) return false;// TODO: error msg
  187. if (!fileInput.files.length) return false;// TODO: error msg
  188. var fileInfo = fileInput.files[0];
  189. console.log('fileInfo', fileInfo);
  190. uploadFileAsBinary(fileInfo);
  191. });
  192. jQuery('#upload_file_as_form_btn').on('click', function() {
  193. var fileInput = document.getElementById('file_input')
  194. if (!fileInput) return false;// TODO: error msg
  195. if (!fileInput.files) return false;// TODO: error msg
  196. if (!fileInput.files.length) return false;// TODO: error msg
  197. var fileInfo = fileInput.files[0];
  198. console.log('fileInfo', fileInfo);
  199. uploadFileAsForm(fileInfo);
  200. });
  201. </script>
  202. <?php
  203. } catch (Exception $e) {
  204. UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage());
  205. }
  206. UI::dol();
  207. }
  208. public function generateFilePathHashFromID($intId) {
  209. // $base36Id = base_convert($intId, 10, 10 + 26);// 0-9 + A-Z
  210. $base36Id = base_convert($intId, 10, 10 + 6);// 0-9 + A-F
  211. $base36Str = str_pad($base36Id, 10, "0", STR_PAD_LEFT);
  212. $base36Str = strrev($base36Str);
  213. $pathParts = array();
  214. $pathParts[] = substr($base36Str, 0, 2);
  215. $pathParts[] = substr($base36Str, 2, 2);
  216. $pathParts[] = substr($base36Str, 4);
  217. $hashPath = implode("/", $pathParts);
  218. echo "ID($intId) converted to ({$base36Id})\t to ({$base36Str})\t to path ({$hashPath})\n";
  219. return $hashPath;
  220. }
  221. public function generatePathTestAction() {
  222. try {
  223. $start = 0;
  224. $start = 446071;
  225. $limit = $start + 100;
  226. echo '<pre>';
  227. for ($i = $start; $i < $limit; $i++) {
  228. $this->generateFilePathHashFromID($i);
  229. }
  230. echo '</pre>';
  231. } catch (Exception $e) {
  232. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  233. }
  234. }
  235. }
  236. /*
  237. CREATE TABLE IF NOT EXISTS `CRM_FILES` (
  238. `ID` int(11) NOT NULL AUTO_INCREMENT,
  239. `FILE_HASH` varchar(255) NOT NULL, -- generated from ID by hash function - only for cache
  240. `FILE_LABEL` varchar(255) NOT NULL, -- original file name or system name
  241. `FILE_TYPE` varchar(32) NOT NULL DEFAULT '', -- $TRG_FILE -> config/.cnf--folders...
  242. `FILE_MTIME` datetime NOT NULL,
  243. `A_STATUS` enum('WAITING','NORMAL','MONITOR','OFF_HARD','OFF_SOFT','DELETED') NOT NULL DEFAULT 'WAITING',
  244. `A_RECORD_CREATE_DATE` datetime DEFAULT NULL,
  245. `A_RECORD_CREATE_AUTHOR` varchar(40) NOT NULL DEFAULT '',
  246. `A_RECORD_UPDATE_DATE` datetime DEFAULT NULL,
  247. `A_RECORD_UPDATE_AUTHOR` varchar(40) NOT NULL DEFAULT '',
  248. `A_ADM_COMPANY` varchar(100) NOT NULL DEFAULT '',
  249. `A_CLASSIFIED` varchar(100) NOT NULL DEFAULT '',
  250. PRIMARY KEY (`ID`),
  251. KEY `FILE_TYPE` (`FILE_TYPE`)
  252. ) ENGINE=MyISAM DEFAULT CHARSET=latin2;
  253. 2016-07-20: 446071 files in /Library/Server/Web/Data/Sites/Default/PLIKI
  254. */