1. <?php
  2. /**
  3. * 备份数据库的扩展类
  4. */
  5. namespace toolkit;
  6. class BackSql
  7. {
  8. private $config = [];
  9. private $handler;
  10. private $tables = array(); //需要备份的表
  11. private $begin; //开始时间
  12. private $error; //错误信息
  13. public function __construct($config)
  14. {
  15. $config['path'] = env('ROOT_PATH') . 'public/static/data/'; //默认目录
  16. $config["sqlbakname"] = date("YmdHis", time()) . ".sql"; //默认保存文件
  17. $this->config = $config;
  18. $this->begin = microtime(true);
  19. header("Content-type: text/html;charset=utf-8");
  20. $this->connect();
  21. }
  22. //首次进行pdo连接
  23. private function connect()
  24. {
  25. try {
  26. $this->handler = new \PDO(
  27. "{$this->config['type']}:host={$this->config['hostname']};port={$this->config['hostport']};dbname={$this->config['database']};",
  28. $this->config['username'],
  29. $this->config['password'],
  30. array(
  31. \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$this->config['charset']};",
  32. \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
  33. \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
  34. )
  35. );
  36. } catch (PDOException $e) {
  37. die("Error!: " . $e->getMessage() . "<br/>");
  38. }
  39. }
  40. /**
  41. * 查询
  42. * @param string $sql
  43. * @return mixed
  44. */
  45. private function query($sql = '')
  46. {
  47. $stmt = $this->handler->query($sql);
  48. $stmt->setFetchMode(\PDO::FETCH_NUM);
  49. $list = $stmt->fetchAll();
  50. return $list;
  51. }
  52. /**
  53. * 获取全部表
  54. * @param string $dbName
  55. * @return array
  56. */
  57. private function get_dbname($dbName = '*')
  58. {
  59. $sql = 'SHOW TABLES';
  60. $list = $this->query($sql);
  61. $tables = array();
  62. foreach ($list as $value) {
  63. $tables[] = $value[0];
  64. }
  65. return $tables;
  66. }
  67. /**
  68. * 获取表定义语句
  69. * @param string $table
  70. * @return mixed
  71. */
  72. private function get_dbhead($table = '')
  73. {
  74. $sql = "SHOW CREATE TABLE `{$table}`";
  75. $ddl = $this->query($sql)[0][1] . ';';
  76. return $ddl;
  77. }
  78. /**
  79. * 获取表数据
  80. * @param string $table
  81. * @return mixed
  82. */
  83. private function get_dbdata($table = '')
  84. {
  85. $sql = "SHOW COLUMNS FROM `{$table}`";
  86. $list = $this->query($sql);
  87. //字段
  88. $columns = '';
  89. //需要返回的SQL
  90. $query = '';
  91. foreach ($list as $value) {
  92. $columns .= "`{$value[0]}`,";
  93. }
  94. $columns = substr($columns, 0, -1);
  95. $data = $this->query("SELECT * FROM `{$table}`");
  96. foreach ($data as $value) {
  97. $dataSql = '';
  98. foreach ($value as $v) {
  99. $dataSql .= "'{$v}',";
  100. }
  101. $dataSql = substr($dataSql, 0, -1);
  102. $query .= "INSERT INTO `{$table}` ({$columns}) VALUES ({$dataSql});\r\n";
  103. }
  104. return $query;
  105. }
  106. /**
  107. * 写入文件
  108. * @param array $tables
  109. * @param array $ddl
  110. * @param array $data
  111. */
  112. private function writeToFile($tables = array(), $ddl = array(), $data = array())
  113. {
  114. $str = "/*\r\nMySQL Database Backup Tools\r\n";
  115. $str .= "Server:{$this->config['hostname']}:{$this->config['hostport']}\r\n";
  116. $str .= "Database:{$this->config['database']}\r\n";
  117. $str .= "Data:" . date('Y-m-d H:i:s', time()) . "\r\n*/\r\n";
  118. $str .= "SET FOREIGN_KEY_CHECKS=0;\r\n";
  119. $i = 0;
  120. foreach ($tables as $table) {
  121. $str .= "-- ----------------------------\r\n";
  122. $str .= "-- Table structure for {$table}\r\n";
  123. $str .= "-- ----------------------------\r\n";
  124. $str .= "DROP TABLE IF EXISTS `{$table}`;\r\n";
  125. $str .= $ddl[$i] . "\r\n";
  126. $str .= "-- ----------------------------\r\n";
  127. $str .= "-- Records of {$table}\r\n";
  128. $str .= "-- ----------------------------\r\n";
  129. $str .= $data[$i] . "\r\n";
  130. $i++;
  131. }
  132. if (!file_exists($this->config['path'])) {
  133. mkdir($this->config['path']);
  134. }
  135. return file_put_contents($this->config['path'] . $this->config['sqlbakname'], $str) ? '备份成功!花费时间' . round(microtime(true) - $this->begin, 2) . 'ms' : '备份失败!';
  136. }
  137. /**
  138. * 设置要备份的表
  139. * @param array $tables
  140. */
  141. private function setTables($tables = array())
  142. {
  143. if (!empty($tables) && is_array($tables)) {
  144. //备份指定表
  145. $this->tables = $tables;
  146. } else {
  147. //备份全部表
  148. $this->tables = $this->get_dbname();
  149. }
  150. }
  151. /**
  152. * 备份
  153. * @param array $tables
  154. * @return bool
  155. */
  156. public function backup($tables = array())
  157. {
  158. //存储表定义语句的数组
  159. $ddl = array();
  160. //存储数据的数组
  161. $data = array();
  162. $this->setTables($tables);
  163. if (!empty($this->tables)) {
  164. foreach ($this->tables as $table) {
  165. $ddl[] = $this->get_dbhead($table);
  166. $data[] = $this->get_dbdata($table);
  167. }
  168. //开始写入
  169. return $this->writeToFile($this->tables, $ddl, $data);
  170. } else {
  171. $this->error = '数据库中没有表!';
  172. return false;
  173. }
  174. }
  175. /**
  176. * 错误信息
  177. * @return mixed
  178. */
  179. public function getError()
  180. {
  181. return $this->error;
  182. }
  183. public function restore($filename = '')
  184. {
  185. $path = $this->config['path'] . $filename;
  186. if (!file_exists($path)) {
  187. $this->error('SQL文件不存在!');
  188. return false;
  189. } else {
  190. $sql = $this->parseSQL($path);
  191. //dump($sql);die;
  192. try {
  193. $this->handler->exec($sql);
  194. echo '还原成功!花费时间', round(microtime(true) - $this->begin, 2) . 'ms';
  195. } catch (PDOException $e) {
  196. $this->error = $e->getMessage();
  197. return false;
  198. }
  199. }
  200. }
  201. /**
  202. * 解析SQL文件为SQL语句数组
  203. * @param string $path
  204. * @return array|mixed|string
  205. */
  206. private function parseSQL($path = '')
  207. {
  208. $sql = file_get_contents($path);
  209. $sql = explode("\r\n", $sql);
  210. //先消除--注释
  211. $sql = array_filter($sql, function ($data) {
  212. if (empty($data) || preg_match('/^--.*/', $data)) {
  213. return false;
  214. } else {
  215. return true;
  216. }
  217. });
  218. $sql = implode('', $sql);
  219. //删除/**/注释
  220. $sql = preg_replace('/\/\*.*\*\//', '', $sql);
  221. return $sql;
  222. }
  223. /**
  224. * 下载备份
  225. * @param string $fileName
  226. * @return array|mixed|string
  227. */
  228. public function downloadFile($fileName)
  229. {
  230. $fileName = $this->config['path'] . $fileName;
  231. if (file_exists($fileName)) {
  232. ob_end_clean();
  233. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  234. header('Content-Description: File Transfer');
  235. header('Content-Type: application/octet-stream');
  236. header('Content-Length: ' . filesize($fileName));
  237. header('Content-Disposition: attachment; filename=' . basename($fileName));
  238. readfile($fileName);
  239. } else {
  240. $this->error = "文件有错误!";
  241. }
  242. }
  243. /**
  244. * 获取文件是时间
  245. * @param string $file
  246. * @return string
  247. */
  248. private function getfiletime($file)
  249. {
  250. $path = $this->config['path'] . $file;
  251. $a = filemtime($path);
  252. $time = date("Y-m-d H:i:s", $a);
  253. return $time;
  254. }
  255. /**
  256. * 获取文件是大小
  257. * @param string $file
  258. * @return string
  259. */
  260. private function getfilesize($file)
  261. {
  262. $perms = stat($this->config['path'] . $file);
  263. $size = $perms['size'];
  264. $a = ['B', 'KB', 'MB', 'GB', 'TB'];
  265. $pos = 0;
  266. while ($size >= 1024) {
  267. $size /= 1024;
  268. $pos++;
  269. }
  270. return round($size, 2) . $a[$pos];
  271. }
  272. /**
  273. * 获取文件列表
  274. * @param string $Order 级别
  275. * @return array
  276. */
  277. public function get_filelist($Order = 0)
  278. {
  279. $FilePath = $this->config['path'];
  280. // print_r($FilePath);die;
  281. $FilePath = opendir($FilePath);
  282. // $FilePath = scandir($FilePath);
  283. $FileAndFolderAyy = array();
  284. $i = 1;
  285. while (false !== ($filename = readdir($FilePath))) {
  286. if ($filename != "." && $filename != "..") {
  287. $i++;
  288. $FileAndFolderAyy[$i]['name'] = $filename;
  289. $FileAndFolderAyy[$i]['time'] = $this->getfiletime($filename);
  290. $FileAndFolderAyy[$i]['size'] = $this->getfilesize($filename);
  291. }
  292. }
  293. $Order == 0 ? sort($FileAndFolderAyy) : rsort($FileAndFolderAyy);
  294. return $FileAndFolderAyy;
  295. }
  296. public function delfilename($filename)
  297. {
  298. $path = $this->config['path'] . $filename;
  299. if (@unlink($path)) {
  300. return '删除成功';
  301. }
  302. }
  303. }

控制器内容:

<?php

namespace app\admin\controller;

use think\Controller;
use toolkit\BackSql;
use think\facade\Config;

class Backup extends Base
{
    //数据库备份
    public function index()
    {
        //获取操作内容:(备份/下载/还原/删除)数据库
        $type = input("type");
        //获取需要操作的数据库名字
        $name = input("name");
        $backup = new BackSql(Config::get("database."));
        switch ($type) {
                //备份
            case "backup":
                $info = $backup->backup();
                $this->success("$info", 'index/backup/bak');
                break;
                //下载
            case "dowonload":
                $info = $backup->downloadFile($name);
                $this->success("$info", 'index/backup/bak');
                break;
                //还原
            case "restore":
                $info = $backup->restore($name);
                $this->success("$info", 'index/backup/bak');
                break;
                //删除
            case "del":
                $info = $backup->delfilename($name);
                $this->success("$info", 'index/backup/bak');
                break;
                //如果没有操作,则查询已备份的所有数据库信息
            default:
                return $this->fetch("index", ["list" => array_reverse($backup->get_filelist())]); //将信息由新到老排序
        }
    }
}

视图中:

{extend name="layout" /}

{block name="title"}新闻列表{/block}

{block name="main"}

<nav class="breadcrumb"><i class="Hui-iconfont">&#xe67f;</i> 首页 <span class="c-gray en">&gt;</span> 新闻管理<span
        class="c-gray en">&gt;</span>
    新闻列表<a class="btn btn-success radius r" style="line-height:1.6em;margin-top:3px"
        href="javascript:location.replace(location.href);" title="刷新"><i class="Hui-iconfont">&#xe68f;</i></a></nav>


<div class="page-container">
    <div class="cl pd-5 bg-1 bk-gray mt-20">
        <span class="l">
            <a class="btn btn-primary " href="{:url('admin_backup',['type'=>'backup'])}"
                onclick="return confirm('备份数据的时间较长,确定要备份所有数据吗?')"><i class="glyphicon glyphicon-plus"></i> <span
                    class="bold">添加备份</span></a>
    </div>

    <div class="">
        <table class="table table-border table-bordered table-hover table-bg ">
            <thead>
                <tr class="text-c">
                    <th>序号</th>
                    <th>备份名称</th>
                    <th>备份时间</th>
                    <th>备份大小</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {volist name='list' id='vo' }
                <tr class="gradeX">
                    <td>{$key+1}</td>
                    <td>{$vo.name}</td>
                    <td>{$vo.time}</td>
                    <td>{$vo.size}</td>
                    <td width="25%">
                        <a href="{:url('admin_backup',['type'=>'dowonload','name'=>$vo.name])}"
                            class="btn btn-success "><i class="glyphicon glyphicon-download-alt"></i> <span
                                class="bold">下载</span></a>
                        <a href="{:url('admin_backup',['type'=>'restore','name'=>$vo.name])}" class="btn btn-info "
                            onclick="return confirm('备份还原后仅会显示当前备份的数据库的信息,您确定还原备份吗 ?')"><i
                                class="glyphicon glyphicon-repeat"></i> <span class="bold">还原</span></a>
                        <a href="{:url('admin_backup',['type'=>'del','name'=>$vo.name])}" class="btn btn-warning"
                            onclick="return confirm('数据库备份一旦删除不可找回,您确定操作吗?')"><i class="fa fa-warning"></i> <span
                                class="admin_backup">删除</span></a>
                    </td>
                </tr>
                {/volist}
            </tbody>
        </table>

    </div>

</div>


</div>
{/block}

{block name="js"}

<script>


</script>
{/block}