153 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			PHP
		
	
	
		
		
			
		
	
	
			153 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			PHP
		
	
	
|  | <?php | |||
|  | namespace app\controller\manager; | |||
|  | 
 | |||
|  | use think\facade\{Db, Config, Env}; | |||
|  | use app\model\{Log as MLog}; | |||
|  | use app\service\Tool; | |||
|  | 
 | |||
|  | class Backup extends Base | |||
|  | { | |||
|  |     /** | |||
|  |      * 因受mysql 配置参数net_buffer_length和max_allowed_packet大小的限制,因此insert语句改为单条插入 | |||
|  |      * show VARIABLES like "%net_buffer_length%" 默认单条sql语句长度16k  | |||
|  |      * show VARIABLES like "%max_allowed_packet%" 默认单个sql文件最大容量 | |||
|  |      * sql 文件注释 -- + 空格之后填写注释内容 | |||
|  |      * 因根目录文件夹权限限制,因此需要手动创建backup/data目录并赋予权限(www用户组) | |||
|  |      */ | |||
|  |     public function back() | |||
|  |     { | |||
|  |         ini_set('max_execution_time', 0); | |||
|  |         ini_set("memory_limit",-1); | |||
|  |         set_time_limit(0); | |||
|  | 
 | |||
|  |         $path = Config::get('filesystem.disks.backup.root') . '/'; | |||
|  |         $dataBase = Env::get('database.database'); | |||
|  |         // 不要使用单引号,双引号中的变量可以解析,单引号就是绝对的字符串
 | |||
|  |         $eol = "\r\n"; | |||
|  |         $eolB = "\r\n\r\n"; | |||
|  |         $info = '-- ------------------------------'.$eol; | |||
|  |         $info .= '-- 日期: '.date('Y-m-d H:i:s',time()).$eol; | |||
|  |         $info .= '-- MySQL --Database - '.$dataBase.$eol; | |||
|  |         $info .= '-- ------------------------------'.$eol; | |||
|  | 
 | |||
|  |         $info .= 'CREATE DATABASE IF NOT EXISTS `'.$dataBase.'` DEFAULT CHARACTER SET "utf8mb4" COLLATE "utf8mb4_general_ci";'.$eolB; | |||
|  |         $info .= 'USE `'.$dataBase.'`;'.$eol; | |||
|  | 
 | |||
|  |         if(is_dir($path)) { | |||
|  |             if(!is_writable($path)) { | |||
|  |                 chmod($path, 0755); | |||
|  |             } | |||
|  |         } else { | |||
|  |             mkdir($path, 0755, true); | |||
|  |         } | |||
|  |         $fileName = $path.$dataBase.'_'.date('YmdHis',time()).'.sql'; | |||
|  |         if(file_exists($fileName)) { | |||
|  |             @unlink($fileName); | |||
|  |         } | |||
|  |         // 以追加的方式写入
 | |||
|  |         file_put_contents($fileName, $info, FILE_APPEND); | |||
|  |         $tables = Db::query('show tables'); | |||
|  |         foreach ($tables as $table) { | |||
|  |             // 表结构
 | |||
|  |             $tableName = $table['Tables_in_'.$dataBase]; | |||
|  |             $sqlTable = 'SHOW CREATE TABLE '.$tableName; | |||
|  |             $res = Db::query($sqlTable); | |||
|  |             $infoTable = '-- ------------------------------'.$eol; | |||
|  |             $infoTable .= '-- Table structure for `'.$tableName.'`'.$eol; | |||
|  |             $infoTable .= '-- ------------------------------'.$eolB; | |||
|  |             $infoTable .= 'DROP TABLE IF EXISTS `'.$tableName.'`;'.$eolB; | |||
|  |             $infoTable .= $res[0]['Create Table'].';'.$eolB; | |||
|  |             // 表数据
 | |||
|  |             $infoTable .= '-- ------------------------------'.$eol; | |||
|  |             $infoTable .= '-- Data for the table `'.$tableName.'`'.$eol; | |||
|  |             $infoTable .= '-- ------------------------------'.$eolB; | |||
|  |             file_put_contents($fileName, $infoTable, FILE_APPEND); | |||
|  | 
 | |||
|  |             $sqlData = 'select * from '.$tableName; | |||
|  |             $data = Db::query($sqlData); | |||
|  |             $count = count($data); | |||
|  |             if ($count > 0) { | |||
|  |                 $dataStr = 'INSERT INTO `'.$tableName.'` VALUE '; | |||
|  |                 foreach ($data as $k => $item) { | |||
|  |                     $valStr = '('; | |||
|  |                     foreach ($item as $val) { | |||
|  |                         // 字符串转义
 | |||
|  |                         $val = addslashes($val); | |||
|  |                         $valStr .= '"'.$val.'", '; | |||
|  |                     } | |||
|  |                     // 去除最后的逗号和空格
 | |||
|  |                     $valStr = substr($valStr,0,strlen($valStr)-2); | |||
|  |                     // 限制单条sql语句在16K以内(可根据mysql配置条件进行调整)
 | |||
|  |                     $sqlLength = strlen($dataStr) + strlen($valStr); | |||
|  |                     if($sqlLength >= (1024 * 16 - 10)) { | |||
|  |                         $dataStr .= $valStr.');'.$eol; | |||
|  |                         file_put_contents($fileName, $dataStr, FILE_APPEND); | |||
|  |                         // 提前分段写入后需重置数据语句
 | |||
|  |                         if ($k <= ($count-1)) { | |||
|  |                             $dataStr = 'INSERT INTO `'.$tableName.'` VALUE '; | |||
|  |                         } else { | |||
|  |                             $dataStr = ''; | |||
|  |                         } | |||
|  |                     } else { | |||
|  |                         if ($k < ($count-1)) { | |||
|  |                             $dataStr .= $valStr.'),'.$eol; | |||
|  |                         } else { | |||
|  |                             $dataStr .= $valStr.');'.$eolB; | |||
|  |                         } | |||
|  |                     } | |||
|  |                 } | |||
|  |                 file_put_contents($fileName, $dataStr, FILE_APPEND); | |||
|  |             } | |||
|  |         } | |||
|  |         clearstatcache(); | |||
|  | 
 | |||
|  |         $backups = explode('/', $fileName); | |||
|  |         $backupName =  $backups[count($backups)-1]; | |||
|  |         $src = Config::get('filesystem.disks.backup.url') . '/'; | |||
|  |         return $this->json(0, '备份成功:' . $src . $backupName); | |||
|  |     } | |||
|  | 
 | |||
|  | 
 | |||
|  |     public function index() | |||
|  |     { | |||
|  |         $path = Config::get('filesystem.disks.backup.root') . '/'; | |||
|  |         $src = Config::get('filesystem.disks.backup.url') . '/'; | |||
|  |         $items = []; | |||
|  |         if(is_dir($path)) { | |||
|  |             if(!is_readable($path)) { | |||
|  |                 chmod($path,0755); | |||
|  |             } | |||
|  |             $files = scandir($path); | |||
|  |             foreach ($files as $file) { | |||
|  |                 if($file != '.' && $file != '..') { | |||
|  |                     $ext = substr($file, -4); | |||
|  |                     if ($ext == '.sql') { | |||
|  |                         $creatTime = substr($file, -18, 14); | |||
|  |                         $items[] = [ | |||
|  |                             'file' => $file, | |||
|  |                             'path' => $src . $file, | |||
|  |                             'time' =>is_numeric($creatTime) ? date('Y-m-d H:i:s', strtotime($creatTime)) : '', | |||
|  |                         ]; | |||
|  |                     } | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  |         clearstatcache(); | |||
|  | 
 | |||
|  |         $this->data['items'] = $items; | |||
|  |         $this->data['backupPath'] = str_replace('\\','/', $path); | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  |      | |||
|  |     public function del() | |||
|  |     { | |||
|  |         if (request()->isPost()) { | |||
|  |             $filePath = input('post.id'); | |||
|  |             Tool::delFile($filePath); | |||
|  |             MLog::write('backup', 'del', '删除了备份数据文件,文件路径为:' . $filePath); | |||
|  |             return $this->json(); | |||
|  |         } else { | |||
|  |             return $this->json(1, '非法请求'); | |||
|  |         } | |||
|  |     } | |||
|  | } |