<?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, '非法请求'); } } }