如何使用 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('刪除備份成功'); } }

浙公網安備 33010602011771號