需求javascript
1.用戶上傳數據庫備份文件(.bak)還原到指定服務器上(若是用戶不上傳文件,則還原默認的備份文件)php
2.還原文件後,建立訪問該數據庫的用戶,登陸名和密碼可由用戶輸入(若是用戶不輸入登陸名和密碼,則生成默認的登陸名和密碼)css
問題html
按照需求,很容易想打使用restore database語句,代碼實現後測試發現,重複還原一個數據庫文件,會提示還原失敗,正在使用該文件。java
由於還原後在文件夾目錄中會有相同的邏輯文件名稱,因此會出現問題。jquery
解決方案web
使用restore database with move還原語句,將每次還原的文件放在不一樣的位置數據庫
代碼api
頁面代碼服務器
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>還原數據庫</title> <link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/webuploader/0.1.5/webuploader.css" /> <style> * { padding: 0; margin: 0; } html, body { height: 100%; font-size: 16px; font-family: 'Microsoft YaHei'; } table { width: 100%; height: 100%; } table td { text-align: center; } td > input { margin: 15px 0; padding: 5px; border: 1px solid #ccc; border-radius: 3px; width: 400px; height: 30px; font-size: 16px; font-family: 'Microsoft YaHei'; } p { line-height: 50px; color: #999; display: none; } .btn { display: inline-block; text-decoration: none; color: #fff; background-color: #0094ff; padding: 10px 15px; border-radius: 3px; } .btn:hover { opacity: 0.9; } .icofile { display: none; color: green; } .bar { width: 100%; height: 10px; background: #999; position: relative; display: none; } .subbar { height: 10px; background: green; position: absolute; top: 0; left: 0; } .webuploader-pick { border: 1px dashed #ccc; padding: 20px 10px 20px 50px; background: url(http://www.easyicon.net/api/resizeApi.php?id=1160478&size=32) no-repeat 10px center; color: #666; } .tips { color: #999; line-height: 40px; } </style> </head> <body> <table> <tr> <td> <div id="uploader" style="width:400px;margin:0 auto;"> <div class="btns"> <div id="picker">選擇備份文件</div> <div class="bar"> <div class="subbar"></div> </div> <div class="filelist"></div> </div> </div> <div class="tips"> 說明:不上傳備份文件,將還原默認的數據庫備份文件 </div> <input type="text" placeholder="還原後數據庫名稱" id="name" /><br /> <input type="text" placeholder="數據庫帳戶,可不填寫" id="account" /><br /> <input type="password" placeholder="數據庫密碼,可不填寫" id="pwd" /><br /> <a href="javascript:;" id="restore" class="btn">一鍵還原</a><br /> <p class="tip">還原時間與文件大小成正比,請耐心等待.....</p> </td> </tr> </table> <script src="http://apps.bdimg.com/libs/jquery/1.7.2/jquery.min.js"></script> <script src="https://cdn.staticfile.org/webuploader/0.1.5/webuploader.js"></script> <script> $(function () { //文件上傳 var uploader = WebUploader.create({ swf: 'https://cdn.staticfile.org/webuploader/0.1.5/Uploader.swf', auto: true, server: '/Home/Upload', pick: { id: '#picker', multiple: false, }, resize: false, fileNumLimit: 1, duplicate: false,//去重 fileSizeLimit: 100 * 1024 * 1024,//100M accept: { title: 'Bak', extensions: 'bak', mimeTypes: 'application/x-trash' } }); //加入隊列前 uploader.on('beforeFileQueued', function (file) { var files = uploader.getFiles(); files.forEach(function (item) { uploader.removeFile(item, true); }); }); //加入隊列時 uploader.on('fileQueued', function (file) { $(".filelist").html("文件:" + file.name); }); //進度條 uploader.on('uploadProgress', function (file, percentage) { $(".bar").show(); $(".subbar").css("width", percentage * 100 + "%"); }); //成功 uploader.on('uploadSuccess', function (file, response) { $(".bar").fadeOut(2000); if (response.message != "OK") { alert(response.message); } }); //失敗 uploader.on('uploadError', function (file, reason) { alert(reason); }); //完成 uploader.on('uploadComplete', function (file) { }); //驗證 uploader.on('error', function (msg) { if (msg == "Q_EXCEED_NUM_LIMIT") { alert("最多選擇一個文件上傳"); } else if (msg == "Q_EXCEED_SIZE_LIMIT") { alert("文件最大不能超過100M"); } else if (msg == "Q_TYPE_DENIED") { alert("文件類型必須是BAK文件"); } else if (msg == "F_DUPLICATE") { alert("隊列中有同名文件了"); } else { alert("未知錯誤:" + msg + ",請聯繫客服"); } }); //還原數據庫文件 $("#restore").click(function () { var valid = true; if ($("#name").val() == "") { valid = false; return false; } if (valid) { $(this).hide(); $(".tip").show(); $.post("/Home/Restore", { name: $("#name").val(), account: $("#account").val(), pwd: $("#pwd").val(), isRestoreDefault: uploader.getFiles().length == 0 }, function (data) { alert(data.msg); window.location.reload(); }); } }); }); </script> </body> </html>
後臺代碼
[HttpPost] public JsonResult Restore(string name, string account, string pwd, bool isRestoreDefault) { using (SqlConnection connection = new SqlConnection("Data Source=.;uid=" + Config("DBUser") + ";pwd=" + Config("DBPwd") + ";database=master;timeout=180")) { try { connection.Open(); string dbNewUserName = name + "User"; string dbNewUserPwd = name + "User!@#"; if (!string.IsNullOrEmpty(account)) { dbNewUserName = account; dbNewUserPwd = pwd; } //備份文件獲取 string path = Server.MapPath("/Content/" + (isRestoreDefault ? "cms" : "temp") + ".bak"); List<string> logicalNameList = new List<string>(); //還原前,獲取數據庫備份文件的邏輯名稱 string cmdText = "restore filelistonly from disk='" + path + "'"; SqlCommand cmd = new SqlCommand(cmdText, connection); cmd.CommandTimeout = Int32.MaxValue;//命令執行時間 SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { logicalNameList.Add(reader["LogicalName"].ToString()); } reader.Close(); //建立數據庫 cmdText = @"restore database " + name + " from disk='" + path + "' with"; string dataPath = Server.MapPath("/RestoreData/"); cmdText += " move '" + logicalNameList[0] + "' to '" + dataPath + name + ".mdf',"; cmdText += " move '" + logicalNameList[1] + "' to '" + dataPath + name + ".ldf'"; cmd = new SqlCommand(cmdText, connection); cmd.ExecuteNonQuery(); //建立用戶 cmd = new SqlCommand("create login " + dbNewUserName + " with password='" + dbNewUserPwd + "', default_database=" + name, connection); cmd.ExecuteNonQuery(); string dbSql = string.Format(@"use {0} create user {1} for login {1} with default_schema=dbo exec sp_addrolemember 'db_owner', '{1}' ", name, dbNewUserName, dbNewUserPwd); cmd = new SqlCommand(dbSql, connection); cmd.ExecuteNonQuery(); //刪除上傳的數據庫文件 if (!isRestoreDefault) { System.IO.File.Delete(path); } return Json(new { msg = "還原成功" }); } catch (Exception e) { return Json(new { msg = "還原失敗:" + e.Message }); } finally { connection.Close(); } } }