├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── eg.gif ├── src ├── OnlineEditor.php ├── ViewFile.php └── viewEditor.html └── tests └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /vendor/ 3 | /logs/ 4 | .htaccess 5 | nginx.htaccess -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wfm 2 | ## 原生PHP在线文件管理器,原生PHP在线文件编辑器 3 | 4 | #### 解决痛点 5 | 在开发中,往往线上项目在查看log日志时,都是远程登录服务器,然后再翻找很深的log文件路径,这样不仅费时费力,而且操作繁琐。本项目实现了以网页的形式直接在线查看log日志,这样不仅仅避免了每次远程登录服务器,再查找log文件的繁琐操作,而且还节约了大量宝贵时间,让你只专注项目业务开发和维护。 6 | 7 | #### 主要功能 8 | 9 | - 支持浏览文件目录 10 | - 支持在线浏览文件内容 11 | - 支持在线创建文件,编辑文件,删除文件 12 | - 文件夹和文件排序 13 | 14 | #### 实用场景 15 | - 网站在线Log日志,查看和管理 16 | - 在线修改代码 17 | - 单纯文件管理 18 | 19 | #### 使用说明 20 | 原生php开发,代码量少,无其他依赖,容易集成到自己项目中,比如你项目是ThinkPHP,CI,Yii等框架开发,都可以很轻松的集成到自己项目中,不用时直接删除即可。 21 | 22 | composer 导入 23 | ```sh 24 | composer require melodyne/wfm 25 | ``` 26 | 27 | 使用示例 28 | ```PHP 29 | $viewFile = new \melodyne\wfm\ViewFile([ 30 | 'sys_name'=>'在线log日志系统', 31 | 'dir_path'=>'../logs', 32 | 'file_action'=>['create','update','delete'] // 创建,修改,删除(都不要的话,留空即可) 33 | ]); 34 | $viewFile->showList(); 35 | exit(); // 中断后续,根据自己框架情况决定是否需要 36 | ``` 37 | 权限 `file_action` 的值为,不要该权限则不填 38 | * create 创建文件权限 39 | * update 修改编辑文件权限 40 | * delete 删除文件权限 41 | 42 | **运行效果** 43 | 44 |  45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melodyne/wfm", 3 | "description": "Online file view management", 4 | "type": "project", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "chenwanzhou", 9 | "email": "chenwanzhou@daishujiankang.com" 10 | } 11 | ], 12 | "minimum-stability": "dev", 13 | "require": { 14 | "php": ">=5.6" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "melodyne\\wfm\\": "src/" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "d71567890e112c61d8716426b5c0d170", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "dev", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=5.6" 17 | }, 18 | "platform-dev": [], 19 | "plugin-api-version": "1.1.0" 20 | } 21 | -------------------------------------------------------------------------------- /eg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melodyne/wfm/c35aed96a3a2c28a3d1a631db8f042c1ad81162e/eg.gif -------------------------------------------------------------------------------- /src/OnlineEditor.php: -------------------------------------------------------------------------------- 1 | filePath = $filePath; 28 | } 29 | 30 | //当本类销毁的时候进行的操作 31 | function __destruct() 32 | { 33 | // echo $this->filePath; 34 | } 35 | 36 | //获取文件的内容 37 | function getContent($filePath) 38 | { 39 | if (!isset($filePath)) { 40 | 41 | } else { 42 | if (filetype($filePath) == 'file') { 43 | $fileContent = file_get_contents($filePath); 44 | return $fileContent; 45 | } 46 | } 47 | } 48 | 49 | //放入文件内容 50 | function putContent($filePath, $fileContent) 51 | { 52 | if(substr($filePath,0,strlen($this->filePath)) !== $this->filePath){ 53 | die('无权操作,非配置目录的文件'); 54 | } 55 | file_put_contents($filePath, $fileContent); 56 | } 57 | 58 | //判断目录是否存在 59 | private function judgeExist() 60 | { 61 | //判断目录是否为空或者没有文件 62 | if (is_dir($this->filePath) && file_exists($this->filePath)) { 63 | return true; 64 | } else { 65 | return false; 66 | } 67 | } 68 | 69 | //创建文件 70 | function createFile($filename) 71 | { 72 | if(substr($filename,0,strlen($this->filePath)) !== $this->filePath){ 73 | die('无权操作,非配置目录的文件'); 74 | } 75 | if (!file_exists($filename)) { 76 | fopen($filename, "w+"); 77 | } else { 78 | echo "点此返回"; 79 | die("文件已经存在," . $filename); 80 | } 81 | 82 | } 83 | 84 | //删除文件 85 | function delFile($filename) 86 | { 87 | if(substr($filename,0,strlen($this->filePath)) !== $this->filePath){ 88 | die('无权操作,非配置目录的文件'); 89 | } 90 | if (file_exists($filename)) { 91 | if(is_dir($filename)){ 92 | if(!rmdir($filename)){ 93 | echo "点此返回"; 94 | echo '删除失败,可能文件下有文件'; 95 | die(); 96 | } 97 | }else{ 98 | unlink($filename); 99 | } 100 | }else{ 101 | die('文件不存在:'.$filename); 102 | } 103 | } 104 | 105 | // 文件排序 106 | function my_sort($arrays, $sort_key, $sort_order = SORT_ASC, $sort_type = SORT_NUMERIC) 107 | { 108 | if ($arrays == []) return []; 109 | if (!is_array($arrays)) return false; 110 | 111 | foreach ($arrays as $array) { 112 | if (is_array($array)) { 113 | $key_arrays[] = $array[$sort_key]; 114 | } else { 115 | return false; 116 | } 117 | } 118 | array_multisort($key_arrays, $sort_order, $sort_type, $arrays); 119 | return $arrays; 120 | } 121 | 122 | /** 123 | * 格式化文件大小显示 124 | * 125 | * @param int $size 126 | * @return string 127 | */ 128 | function formatSize($size) 129 | { 130 | $prec = 3; 131 | $size = round(abs($size)); 132 | $units = array( 133 | 0 => " B ", 134 | 1 => " KB", 135 | 2 => " MB", 136 | 3 => " GB", 137 | 4 => " TB" 138 | ); 139 | if ($size == 0) { 140 | return str_repeat(" ", $prec) . "0$units[0]"; 141 | } 142 | $unit = min(4, floor(log($size) / log(2) / 10)); 143 | $size = $size * pow(2, -10 * $unit); 144 | $digi = $prec - 1 - floor(log($size) / log(10)); 145 | $size = round($size * pow(10, $digi)) * pow(10, -$digi); 146 | 147 | return $size . $units[$unit]; 148 | } 149 | 150 | //主函数 151 | function main() 152 | { 153 | if ($this->judgeExist()) { 154 | //获取打开文件夹对象 155 | $fileOpen = opendir($this->filePath); 156 | $dirArr = array(); 157 | $fileArr = array(); 158 | $i = 1; 159 | //遍历文件夹 160 | while ($file = readdir($fileOpen)) { 161 | //过滤 162 | if (in_array($file, $this->fileFilter)) { 163 | continue; 164 | } 165 | $path = rtrim($this->filePath, '/') . '/' . $file; 166 | $type = fileType($path); 167 | $size = $this->formatSize(fileSize($path)); 168 | if ($type == 'dir') { // 文件夹 169 | $dirArr[] = array( 170 | 'fileCode' => $i, 171 | 'fileName' => $file, 172 | 'fileType' => $type, 173 | 'fileSize' => $size, 174 | 'filemtime' => filemtime($path) 175 | ); 176 | } else { // 文件 177 | $fileArr[] = array( 178 | 'fileCode' => $i, 179 | 'fileName' => $file, 180 | 'fileType' => $type, 181 | 'fileSize' => $size, 182 | 'filemtime' => filemtime($path) 183 | ); 184 | } 185 | $i++; 186 | } 187 | $dirArr = $this->my_sort($dirArr, 'fileName', SORT_ASC, SORT_STRING); 188 | $fileArr = $this->my_sort($fileArr, 'fileName', SORT_ASC, SORT_STRING); 189 | closedir($fileOpen); //关闭文件 190 | return array_merge($dirArr, $fileArr); 191 | 192 | } else { 193 | die("不存在此文件夹"); 194 | } 195 | } 196 | 197 | } 198 | 199 | ?> -------------------------------------------------------------------------------- /src/ViewFile.php: -------------------------------------------------------------------------------- 1 | dirPath = $config; 17 | }else{ 18 | if(empty($config['dir_path'])){ 19 | die('请配置要读取的文件夹路径'); 20 | } 21 | $this->dirPath = $config['dir_path']; 22 | if(isset($config['sys_name']) && $config['sys_name']){ 23 | $this->systemName = $config['sys_name']; 24 | } 25 | if(isset($config['file_action']) && $config['file_action']){ 26 | $this->fileAction = array_merge($this->fileAction,$config['file_action']); 27 | } 28 | } 29 | } 30 | 31 | function showList() 32 | { 33 | $sysName = $this->systemName; 34 | $dirPath = $this->dirPath; 35 | $fileAction = $this->fileAction; 36 | if (isset($_GET['dir'])) { 37 | $dirPath = $_GET['dir']; 38 | } 39 | $action = null; 40 | 41 | //获得onlineEditor对象 42 | $onlineEditor = new OnlineEditor($dirPath); 43 | $fileMes = $onlineEditor->main(); 44 | 45 | //处理文件路径 46 | function subFilePath($dirPath, $filename) 47 | { 48 | $dirPath = rtrim($dirPath,'/'); 49 | $dirPath = rtrim($dirPath,'\\'); 50 | return $dirPath .'/'. $filename; 51 | } 52 | 53 | //初始化 54 | if (array_key_exists('action', $_GET)) { 55 | if(!in_array($_GET['action'],$fileAction)){ 56 | echo "点此返回"; 57 | die('无操作权限'); 58 | } 59 | switch ($_GET['action']) { 60 | case 'open': 61 | $action = 'open'; 62 | break; 63 | case 'look': 64 | $action = 'look'; 65 | break; 66 | case 'update': 67 | $action = 'update'; 68 | break; 69 | case 'delete': 70 | $onlineEditor->delFile(subFilePath($dirPath, $_GET['filename'])); 71 | $action = 'delete'; 72 | echo subFilePath($dirPath, $_GET['filename']); 73 | echo ""; 74 | break; 75 | } 76 | } else { 77 | $action = null; 78 | } 79 | 80 | if (array_key_exists('action', $_POST)) { 81 | if(!in_array($_POST['action'],$fileAction)){ 82 | echo "点此返回"; 83 | die('无操作权限'); 84 | } 85 | switch ($_POST['action']) { 86 | case 'create': 87 | $onlineEditor->createFile(subFilePath($dirPath, $_POST['filename'])); 88 | echo ""; 89 | break; 90 | } 91 | } 92 | 93 | //获取文件内容 94 | if (array_key_exists('filename', $_GET) && ($_GET['action'] == 'update' || $_GET['action'] == 'look')) { 95 | $root = subFilePath(rtrim($dirPath, '/') . '/', $_GET['filename']); 96 | $fileContent = $onlineEditor->getContent($root); 97 | } else { 98 | $fileContent = "文件路径错误,或操作类型不存在"; 99 | } 100 | 101 | if (array_key_exists('filecontent', $_POST)) { 102 | $onlineEditor->putContent(subFilePath($dirPath, $_POST['filename']), $_POST['filecontent']); 103 | echo ""; 104 | } 105 | 106 | // url路径处理 107 | $requestUri = $_SERVER["REQUEST_URI"]; 108 | if(strstr($requestUri,'?')){ 109 | $requestUri .= '&'; 110 | }else{ 111 | $requestUri .= '?'; 112 | } 113 | 114 | //引入界面 115 | require __DIR__."/viewEditor.html"; 116 | 117 | } 118 | } 119 | 120 | ?> -------------------------------------------------------------------------------- /src/viewEditor.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |