|
|
@@ -0,0 +1,285 @@
|
|
|
+<?php
|
|
|
+// @requires $_SERVER['SERVER_NAME']
|
|
|
+
|
|
|
+Lib::loadClass('RouteBase');
|
|
|
+Lib::loadClass('Schema_TableFactory');
|
|
|
+Lib::loadClass('Response');
|
|
|
+Lib::loadClass('UI');
|
|
|
+Lib::loadClass('OBJ');
|
|
|
+
|
|
|
+/*
|
|
|
+# FileStorage:
|
|
|
+- [x] create CRM_FILES - sql at the end of file
|
|
|
+ - [x] only meta fields, perms? - no perms in relation based on record which is connected
|
|
|
+- [x] add file to storage - API POST/PUT action
|
|
|
+ - [ ] base storage folder - from config - default /Library/Server/Web/Data/p5-pliki, chmod - add to instalator, upgrade
|
|
|
+ - [x] subfolders like postfix, but [0-9],[A-Z] - substr(strrev(base_convert($id, 10, 10 + 26)), 0, 1) - 36 folderów
|
|
|
+ - [x] file connected to user - A_RECORD_CREATE_AUTHOR
|
|
|
+- [ ] connect file to record - {tbl_name}__REF__FILES
|
|
|
+
|
|
|
+# upload API: `index.php?_route=FileStorage&_task=upload&name={file_name}`
|
|
|
+
|
|
|
+*/
|
|
|
+class Route_FileStorage extends RouteBase {
|
|
|
+
|
|
|
+ public function handleAuth() {
|
|
|
+ if (!User::logged()) {
|
|
|
+ User::authByRequest();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function defaultAction() {
|
|
|
+ UI::gora();
|
|
|
+ UI::menu();
|
|
|
+ try {
|
|
|
+ echo '...';
|
|
|
+ } catch (Exception $e) {
|
|
|
+ UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage());
|
|
|
+ }
|
|
|
+ UI::dol();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function addFile($content, $name = '') {
|
|
|
+ $rootFileStoragePath = '/tmp/test-upload-file-storage';// TODO: from config!
|
|
|
+
|
|
|
+ $sqlLogin = User::getLogin();
|
|
|
+ $sqlLabel = DB::getPDO()->quote($sqlLabel, PDO::PARAM_STR);
|
|
|
+ $sql = "
|
|
|
+ insert into CRM_FILES (`A_RECORD_CREATE_AUTHOR`,`A_RECORD_CREATE_DATE`,`FILE_LABEL`)
|
|
|
+ values ('{$sqlLogin}', NOW(), {$sqlLabel})
|
|
|
+ ";
|
|
|
+ DBG::_('DBG', '>2', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ DB::getPDO()->exec($sql);
|
|
|
+ $dbLastId = DB::getPDO()->lastInsertId();
|
|
|
+ DBG::_('DBG', '>1', "dbLastId", $dbLastId, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ $filePath = $this->generateFilePathHashFromID($dbLastId);
|
|
|
+ DBG::_('DBG', '>1', "filePath", $filePath, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+
|
|
|
+ $absFilePath = "{$rootFileStoragePath}/{$filePath}";
|
|
|
+ $dirPath = dirname($absFilePath);
|
|
|
+ @mkdir($dirPath, $mode = 0777, $recursive = true);
|
|
|
+ if (!file_exists($dirPath)) throw new Exception("Cannot create path");
|
|
|
+ @chmod($dirPath, $mode = 0777);
|
|
|
+
|
|
|
+ $fp = fopen($absFilePath, 'w');
|
|
|
+ fwrite($fp, $fileContent);
|
|
|
+ fclose($fp);
|
|
|
+
|
|
|
+ if (!file_exists($dirPath)) throw new Exception("Cannot save file");
|
|
|
+ }
|
|
|
+
|
|
|
+ public function uploadAction() {
|
|
|
+ try {
|
|
|
+ $fileContent = Request::getRequestBody();
|
|
|
+ $sqlLabel = V::get('name', '', $_REQUEST);
|
|
|
+ $this->addFile($fileContent, $sqlLabel);
|
|
|
+ echo 'file uploaded';
|
|
|
+ } catch (Exception $e) {
|
|
|
+ echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function uploadFromBinaryTestAction() {
|
|
|
+ try {
|
|
|
+ $fileContent = Request::getRequestBody();
|
|
|
+ $filePath = '/tmp/test-upload-data.txt';
|
|
|
+ $fp = fopen($filePath, 'w');
|
|
|
+ fwrite($fp, $fileContent);
|
|
|
+ fclose($fp);
|
|
|
+
|
|
|
+ echo 'file uploaded?';
|
|
|
+ } catch (Exception $e) {
|
|
|
+ echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function downloadTestAction() {
|
|
|
+ try {
|
|
|
+ $filePath = '/tmp/test-upload-data.txt';
|
|
|
+ if (!file_exists($filePath)) throw new Exception("file not exists!");
|
|
|
+ header('Content-Description: File Transfer');
|
|
|
+ header('Content-Type: application/octet-stream');
|
|
|
+ header('Content-Disposition: attachment; filename="'.basename($filePath).'"');
|
|
|
+ header('Expires: 0');
|
|
|
+ header('Cache-Control: must-revalidate');
|
|
|
+ header('Pragma: public');
|
|
|
+ header('Content-Length: ' . filesize($filePath));
|
|
|
+ readfile($filePath);
|
|
|
+ exit;
|
|
|
+ } catch (Exception $e) {
|
|
|
+ echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function uploadFromFormTestAction() {
|
|
|
+ try {
|
|
|
+ // $fileContent = Request::getRequestBody();
|
|
|
+ DBG::_(true, true, '_POST', $_POST, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+
|
|
|
+ // $filePath = '/tmp/test-upload-data.txt';
|
|
|
+ // $fp = fopen($filePath, 'w');
|
|
|
+ // fwrite($fp, $fileContent);
|
|
|
+ // fclose($fp);
|
|
|
+
|
|
|
+ // echo 'file uploaded?';
|
|
|
+ } catch (Exception $e) {
|
|
|
+ echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function uploadFormTestAction() {
|
|
|
+ UI::gora();
|
|
|
+ UI::menu();
|
|
|
+ try {
|
|
|
+ // multiple: <input type="file" id="file_input" multiple="multiple" />
|
|
|
+ // only images: <input type="file" id="file_input" multiple="multiple" accept="image/*" />
|
|
|
+ ?>
|
|
|
+<div class="container">
|
|
|
+ <form>
|
|
|
+ <input type="file" id="file_input" style="display:block; width:100%; height:200px; background-color:silver; text-align:center">
|
|
|
+ </form>
|
|
|
+ <button class="btn btn-primary" id="upload_file_as_binary_btn">upload as binary</button>
|
|
|
+ <button class="btn btn-default" id="upload_file_as_form_btn">upload as form</button>
|
|
|
+ <a class="btn btn-default" href="index.php?_route=FileStorage&_task=downloadTest" target="_blank">download</a>
|
|
|
+</div>
|
|
|
+<script>
|
|
|
+document.getElementById('file_input').addEventListener('change', function() {
|
|
|
+ for (var i = 0; i < this.files.length; i++){
|
|
|
+ var file = this.files[i];
|
|
|
+ // This code is only for demo ...
|
|
|
+ console.group("File "+i);
|
|
|
+ console.log("name : " + file.name);
|
|
|
+ console.log("size : " + file.size);
|
|
|
+ console.log("type : " + file.type);
|
|
|
+ console.log("date : " + file.lastModified);
|
|
|
+ console.groupEnd();
|
|
|
+ }
|
|
|
+}, false);
|
|
|
+
|
|
|
+function uploadFileAsForm(file) {
|
|
|
+ var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=uploadFromFormTest"; ?>';
|
|
|
+ var xhr = new XMLHttpRequest();
|
|
|
+ var fd = new FormData();
|
|
|
+ xhr.open("POST", serverUrl, true);
|
|
|
+ xhr.onreadystatechange = function() {
|
|
|
+ if (xhr.readyState == 4 && xhr.status == 200) {
|
|
|
+ // Every thing ok, file uploaded
|
|
|
+ console.log(xhr.responseText); // handle response.
|
|
|
+ }
|
|
|
+ };
|
|
|
+ fd.append("upload_file", file);
|
|
|
+ xhr.send(fd);
|
|
|
+}
|
|
|
+function uploadFileAsBinary(file) {
|
|
|
+ var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=uploadFromBinaryTest"; ?>';
|
|
|
+ var serverUrl = '<?php echo Request::getPathUri() . "index.php?_route=FileStorage&_task=upload"; ?>';
|
|
|
+ var _dbg = true;
|
|
|
+
|
|
|
+ // .set('Accept', 'application/json')
|
|
|
+ superagent.post(serverUrl + '&name=' + file.name)
|
|
|
+ .set('Content-Type', file.type)
|
|
|
+ .send(file)
|
|
|
+ .end(function(err, res) {
|
|
|
+ if(_dbg)console.log('DBG: res:', res, 'res.body:', res.body);
|
|
|
+ })
|
|
|
+
|
|
|
+return
|
|
|
+
|
|
|
+ jQuery.ajax({
|
|
|
+ type: "POST",
|
|
|
+ beforeSend: function (request) {
|
|
|
+ request.setRequestHeader("Content-Type", file.type);
|
|
|
+ },
|
|
|
+ url: serverUrl,
|
|
|
+ data: file,
|
|
|
+ processData: false,
|
|
|
+ contentType: false,
|
|
|
+ success: function (data) {
|
|
|
+ console.log("File available at: ", data);
|
|
|
+ },
|
|
|
+ error: function (data) {
|
|
|
+ var obj = jQuery.parseJSON(data);
|
|
|
+ alert(obj.error);
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+jQuery('#upload_file_as_binary_btn').on('click', function() {
|
|
|
+ var fileInput = document.getElementById('file_input')
|
|
|
+ if (!fileInput) return false;// TODO: error msg
|
|
|
+ if (!fileInput.files) return false;// TODO: error msg
|
|
|
+ if (!fileInput.files.length) return false;// TODO: error msg
|
|
|
+ var fileInfo = fileInput.files[0];
|
|
|
+ console.log('fileInfo', fileInfo);
|
|
|
+ uploadFileAsBinary(fileInfo);
|
|
|
+});
|
|
|
+jQuery('#upload_file_as_form_btn').on('click', function() {
|
|
|
+ var fileInput = document.getElementById('file_input')
|
|
|
+ if (!fileInput) return false;// TODO: error msg
|
|
|
+ if (!fileInput.files) return false;// TODO: error msg
|
|
|
+ if (!fileInput.files.length) return false;// TODO: error msg
|
|
|
+ var fileInfo = fileInput.files[0];
|
|
|
+ console.log('fileInfo', fileInfo);
|
|
|
+ uploadFileAsForm(fileInfo);
|
|
|
+});
|
|
|
+</script>
|
|
|
+ <?php
|
|
|
+ } catch (Exception $e) {
|
|
|
+ UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage());
|
|
|
+ }
|
|
|
+ UI::dol();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function generateFilePathHashFromID($intId) {
|
|
|
+ // $base36Id = base_convert($intId, 10, 10 + 26);// 0-9 + A-Z
|
|
|
+ $base36Id = base_convert($intId, 10, 10 + 6);// 0-9 + A-F
|
|
|
+ $base36Str = str_pad($base36Id, 10, "0", STR_PAD_LEFT);
|
|
|
+ $base36Str = strrev($base36Str);
|
|
|
+ $pathParts = array();
|
|
|
+ $pathParts[] = substr($base36Str, 0, 2);
|
|
|
+ $pathParts[] = substr($base36Str, 2, 2);
|
|
|
+ $pathParts[] = substr($base36Str, 4);
|
|
|
+ $hashPath = implode("/", $pathParts);
|
|
|
+ echo "ID($intId) converted to ({$base36Id})\t to ({$base36Str})\t to path ({$hashPath})\n";
|
|
|
+ return $hashPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function generatePathTestAction() {
|
|
|
+ try {
|
|
|
+ $start = 0;
|
|
|
+ $start = 446071;
|
|
|
+ $limit = $start + 100;
|
|
|
+ echo '<pre>';
|
|
|
+ for ($i = $start; $i < $limit; $i++) {
|
|
|
+ $this->generateFilePathHashFromID($i);
|
|
|
+ }
|
|
|
+ echo '</pre>';
|
|
|
+ } catch (Exception $e) {
|
|
|
+ echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+
|
|
|
+CREATE TABLE IF NOT EXISTS `CRM_FILES` (
|
|
|
+ `ID` int(11) NOT NULL AUTO_INCREMENT,
|
|
|
+ `FILE_HASH` varchar(255) NOT NULL, -- generated from ID by hash function - only for cache
|
|
|
+ `FILE_LABEL` varchar(255) NOT NULL, -- original file name or system name
|
|
|
+ `FILE_TYPE` varchar(32) NOT NULL DEFAULT '', -- $TRG_FILE -> config/.cnf--folders...
|
|
|
+ `FILE_MTIME` datetime NOT NULL,
|
|
|
+ `A_STATUS` enum('WAITING','NORMAL','MONITOR','OFF_HARD','OFF_SOFT','DELETED') NOT NULL DEFAULT 'WAITING',
|
|
|
+ `A_RECORD_CREATE_DATE` datetime DEFAULT NULL,
|
|
|
+ `A_RECORD_CREATE_AUTHOR` varchar(40) NOT NULL DEFAULT '',
|
|
|
+ `A_RECORD_UPDATE_DATE` datetime DEFAULT NULL,
|
|
|
+ `A_RECORD_UPDATE_AUTHOR` varchar(40) NOT NULL DEFAULT '',
|
|
|
+ `A_ADM_COMPANY` varchar(100) NOT NULL DEFAULT '',
|
|
|
+ `A_CLASSIFIED` varchar(100) NOT NULL DEFAULT '',
|
|
|
+ PRIMARY KEY (`ID`),
|
|
|
+ KEY `FILE_TYPE` (`FILE_TYPE`)
|
|
|
+) ENGINE=MyISAM DEFAULT CHARSET=latin2;
|
|
|
+
|
|
|
+2016-07-20: 446071 files in /Library/Server/Web/Data/Sites/Default/PLIKI
|
|
|
+
|
|
|
+*/
|