如何使用 PHP 进行数据库备份和恢复?Tinkphp5.0数据库备份、还原和下载完整示例
导读:
参考了很多前辈们的代码,才最终做出来。我的数据库比较小,所以没有弄分卷备份,做开发嘛,都是根据实际需求做的,代码简单实用就好!先上两张效果图给大家看看:
一、先写一个备份数据库类 Backup.php
小白可能会有疑问,为什么我要单独写成一个类?因为这个方法可能会多次调用,写成一个类,可以减少代码的重用,比如说手动备份要调用,每天定时自动备份也需要调用。好了,直接上代码:
<?php namespace app\common; use think\Db; use think\Model; use think\Cookie; use think\Exception; /** * 备份数据库类 */ class Backup { /* * 导出数据库到sql文件 * @param $string $dir 备份保存目录(可选,默认为data) */ public static function export($dir='data') { //保存路径,默认为站点根目录下的data文件夹里 $path = ROOT_PATH .$dir.DIRECTORY_SEPARATOR; //获取数据库名 $dbname=config("database.database"); //检查目录是否存在 if(!is_dir($path)) { //新建目录 mkdir($path, 0777, true); } //检查目录是否可写 if(!is_writable($path)) { chmod($path,0777); } //检查文件是否存在 $fileName=$dbname.'_'.date("Y-m-d",time()).'.sql'; //数据库保存相对路径 $filePath = $dir.DIRECTORY_SEPARATOR.$fileName; //硬盘保存绝对路径 $savepath = $path.$fileName; try{ if(!file_exists($savepath)) { //获取mysql版本 $version = Db::query("select version() as ver"); $ver = $version[0]['ver']; $info = "-- ----------------------------\r\n"; $info .= "-- 日期:".date("Y-m-d H:i:s",time())."\r\n"; $info .= "-- MySQL - ".$ver." : Database - ".$dbname."\r\n"; $info .= "-- ----------------------------\r\n\r\n"; $info .= "CREATE DATAbase IF NOT EXISTS `".$dbname."` DEFAULT CHARACTER SET utf8 ;\r\n\r\n"; $info .= "USE `".$dbname."`;\r\n\r\n"; file_put_contents($savepath,$info,FILE_APPEND); //查询所有表 $sql="show tables"; //执行原生SQL语句 $result=Db::query($sql); foreach ($result as $k=>$v) { //查询表结构 $table = $v['Tables_in_'.$dbname]; $sql_table = "show create table ".$table; $res = Db::query($sql_table); $info_table = "-- ----------------------------\r\n"; $info_table .= "-- Table structure for `".$table."`\r\n"; $info_table .= "-- ----------------------------\r\n\r\n"; $info_table .= "DROP TABLE IF EXISTS `".$table."`;\r\n\r\n"; $info_table .= $res[0]['Create Table'].";\r\n\r\n"; //查询表数据 $info_table .= "-- ----------------------------\r\n"; $info_table .= "-- Data for the table `".$table."`\r\n"; $info_table .= "-- ----------------------------\r\n\r\n"; file_put_contents($savepath,$info_table,FILE_APPEND); $sql_data = "select * from ".$table; $data = Db::query($sql_data); $count= count($data); if($count<1) continue; foreach ($data as $key => $value) { $sqlStr = "INSERT INTO `{$table}` VALUES ("; foreach($value as $column) { //对单引号和换行符进行一下转义 $column = str_replace( array("'","\r\n"), array("\'","\\r\\n"),$column); //当值为空时,使用default默认 $sqlStr .=empty($column) ? "default, " : "'".$column."', "; } //去掉最后一个逗号和空格 $sqlStr = substr($sqlStr,0,strlen($sqlStr)-2); $sqlStr .= ");\r\n"; //$sqlStr = "INSERT INTO `{$table}` VALUES ('" . str_replace(array("\r", "\n"), array('\\r', '\\n'), implode("', '", $value)) . "');\n"; file_put_contents($savepath,$sqlStr,FILE_APPEND); } $info = "\r\n"; file_put_contents($savepath,$info,FILE_APPEND); } //计算文件大小 $size=filesize($savepath); return array('code' => 1, 'msg' => '数据库备份成功','name'=>$fileName,'size'=>$size,'path'=>$filePath); }else{ return array('code' => 0, 'msg' => '备份文件已存在'); } } catch (\Exception $e) { return array('code' => 0, 'msg' => '备份数据库失败,Error:'.$e); } } /** * 将sql文件导入到数据库 * * @param string $sqlfile sql文件 */ public static function import($sqlfile) { if(!file_exists($sqlfile)) { return array('code' => 0, 'msg' => '备份文件不存在'); } try { // 创建保存sqlsql语句的数组 $sqls = array (); $file = fopen ($sqlfile, "rb" ); // 创建表缓冲变量 $table_buffer = ''; while ( ! feof ($file) ) { // 读取每一行sql $row = fgets ( $file); // 如果结尾没有包含';'(即为一个完整的sql语句,这里是插入语句),并且不包含'ENGINE='(即创建表的最后一句) if (! preg_match ( '/;/', $row ) || preg_match ( '/ENGINE=/', $row )) { // 将本次sql语句与创建表sql连接存起来 $table_buffer .= $row; // 如果包含了创建表的最后一句 if (preg_match ( '/ENGINE=/', $table_buffer)) { //执行sql语句创建表 Db::execute($table_buffer); // 清空当前,准备下一个表的创建 $table_buffer = ''; } // 跳过本次 continue; } //执行sql语句 Db::execute($row); } fclose ($file); return array('code' => 1, 'msg' => '还原数据库成功'); } catch (\Exception $e) { return array('code' => 0, 'msg' => '还原数据库失败,Error:'.$e); } } }
二、创建一张数据库表,用于保存备份内容
-- ---------------------------- -- Table structure for `database` -- ---------------------------- DROP TABLE IF EXISTS `database`; CREATE TABLE `database` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, `size` varchar(20) DEFAULT '0', `path` varchar(255) DEFAULT NULL, `create_time` int(11) DEFAULT NULL, `update_time` int(11) DEFAULT NULL, `delete_time` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
三、备份数据库
thinkphp后端控制器代码
<?php namespace app\admin\controller; use think\Controller; use think\Db; use app\common\Backup; /** ** @name='备份' */ public function backup() { if(request()->isPost()){ $res=Backup::export(); if($res['code']==1){ //写入数据库 DatabaseModel::create([ 'name' => $res['name'], 'size' => $res['size'], 'path' => $res['path'] ]); $this->success($res['msg']); }else{ $this->error($res['msg']); } } }
四、下载备份文件
1、下载数据库备份文件,需要先将.sql文件压缩成zip,因此建议前端配合使用jszip插件,jszip教程参数这篇文章:https://blog.csdn.net/qq15577969/article/details/115549729
Ps:有的朋友可能会说,直接使用php自带的zip压缩功能不就可以了吗,何必那么麻烦。这个当然也可以,但前提是你使用的php版本安装了php_zip.dll扩展,然后才能使用ZipArchive类实现zip压缩功能。博主使用的php5.6版本是没有这个扩展的,而且博主开发的系统是要授权给用户使用的,用户不一定都懂安装php扩展,所以为了保险起见,直接选择了配合前端jszip插件来实现压缩的方案。
2、前端js代码:
<script type="text/javascript" src="./jszip.min.js"> </script> <script type="text/javascript"> $.post('后端下载的地址', { 'token': '{$token}', 'id': data.id }, function(res) { if (res.code == 1) { var zip = new JSZip(); //res.name是文件名称,res.content是文件内容 zip.file(res.name, res.content); // 生成zip文件并下载 zip.generateAsync({ type: 'blob', // 压缩类型 compression: "DEFLATE", // STORE:默认不压缩 DEFLATE:需要压缩 compressionOptions: { level: 9 // 压缩等级1~9 1压缩速度最快,9最优压缩方式 } }).then(function(content) { // 下载的文件名 var filename = res.name + '.zip'; // 创建隐藏的可下载链接 var eleLink = document.createElement('a'); eleLink.download = filename; eleLink.style.display = 'none'; // 下载内容转变成blob地址 eleLink.href = URL.createObjectURL(content); // 触发点击 document.body.appendChild(eleLink); eleLink.click(); // 然后移除 document.body.removeChild(eleLink); }); } else { mui.toast(res.msg); } }); </script>
3、thinkphp后端控制器代码:
<?php /** ** @name='下载' */ public function down() { if(request()->isPost()){ $data=input('post.'); !isset($data['id']) && $this->error('参数非法'); !is_numeric(trim($data['id'])) && $this->error('参数非法'); $file = DatabaseModel::field('name,path')->where('id',intval($data['id']))->find(); empty($file) && $this->error('备份数据不存在'); $path=ROOT_PATH.$file['path']; //读取文件 $content=file_get_contents($path); if (!empty($content)) { $arr=[ 'code'=>1, 'name'=>$file['name'], 'content'=>$content ]; header('Content-Type:application/json; charset=utf-8'); echo json_encode($arr, JSON_UNESCAPED_UNICODE); exit; }else{ $this->error('备份文件被损坏'); } } }
五、还原备份文件
1、还原数据库需要修改database.php里的数据库配置,如下:
// 数据库连接参数 'params' => [ 'MYSQL_ATTR_USE_BUFFERED_QUERY' => true, ],
2、thinkphp后端控制器代码:
/** ** @name='还原' */ public function recover() { if(request()->isPost()){ $data=input('post.'); !isset($data['id']) && $this->error('参数非法'); !is_numeric(trim($data['id'])) && $this->error('参数非法'); $file = DatabaseModel::field('id,name,path')->where('id',intval($data['id']))->find(); empty($file) && $this->error('备份数据不存在'); //.sql文件的全路径 $path=ROOT_PATH.$file['path']; $res=Backup::import($path); if($res['code']==1){ $result=DatabaseModel::where('id',intval($file['id']))->find(); if(!$result){ //删除备份文件 unlink($path); } $this->success($res['msg']); }else{ $this->error($res['msg']); } } }
3、还原的时候,一定要先给用户警告提示,否则会导致数据丢失,如下:
六、删除备份文件
thinkphp后端控制器代码:
/** ** @name='删除' */ public function del() { if(request()->isPost()){ $data=input('post.'); !isset($data['id']) && $this->error('参数非法'); !is_numeric(trim($data['id'])) && $this->error('参数非法'); Db::startTrans(); try{ $file = DatabaseModel::field('path')->where('id',intval($data['id']))->find(); $path=ROOT_PATH.$file['path']; if(file_exists($path)){ //thinkphp使用unlink函数来删除文件,参数是文件的地址 unlink($path); } DatabaseModel::where('id',intval($data['id']))->delete(); Db::commit(); } catch (\Exception $e) { Db::rollback(); $this->error('删除备份失败'); } $this->success('删除备份成功'); } }