1. 程式人生 > >ASP.NET CORE Linux發布工具(文件對比 只上傳差異文件;自動啟停WebServer命令;上傳完成自動預熱WebServer)

ASP.NET CORE Linux發布工具(文件對比 只上傳差異文件;自動啟停WebServer命令;上傳完成自動預熱WebServer)

pps 手動更新 ftpclient centos paths config AS direct tco

最近這幾日在搞一個小網站:教你啊 ;(感興趣的朋友可以來捧場,在這個網站上有任何消費我都可以退還)由於更新頻繁,手動更新特別麻煩,於是開發了這個小工具用了一段時間,還是挺順手的,同時.NET CoreQQ群(225982985)的群友 @亡我之心不死 也推薦我分享出來這就把代碼公布在這裏,有什麽問題可以聯系我:
技術分享圖片
先看配置文件App.Config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="ServerIPAddress" value="***.***.***.***"/>
<add key="SSHUserName" value="***"/>
<add key="SSHPassWord" value="*********"/>
<add key="ServerPath" value="/***/***/"/>
<add key="ClientPath" value="D:\p\JiaoNiA\code\JNA\JNA.Web\bin\Release\netcoreapp2.0\centos.7-x64\publish\"/>
<add key="IgnorFilePatten" value="System\..+;Microsoft\..+;.+\.so" />
<add key="HttpServerStartCommand" value="systemctl start jna.service"/>
<add key="HttpServerStopCommand" value="systemctl stop jna.service"/>
<add key="WebSiteUrl" value="http://www.jiaonia.com"/>
<add key="WebSiteAssertString" value="教你啊-知識復利制造平臺"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
ServerIPAddress:服務器地址,服務器環境只支持linuxSSHUserName:服務器ssh的用戶名(同時也得是sftp的用戶名)SSHPassWord:服務器ssh的密碼(同時也得是sftp的密碼)ServerPath:服務器WEB程序的部署路徑ClientPath:你的開發環境,VS編譯之後的路徑,我用的編譯命令是:
dotnet publish -c release -r centos.7-x64   
IgnorFilePatten:由於VS編譯後的文件非常多,有些文件上傳一次,一輩子也不用再上傳了,那麽就可以在這裏設置一些正則表達式,過濾這些文件,減少比對工作量(正則表達式是用分號分割的)HttpServerStopCommand:大部分時候更新程序都需要停機更新,這個命令就是停止WebServer的命令HttpServerStartCommand:這個命令是升級完成後啟動WebServer的命令WebSiteUrl:升級完成後,並且WebServer也成功重啟了,這個程序會請求一下你的web程序的URL,用來預熱程序,要不然第一次訪問很慢,這個URL就是在這裏設置的WebSiteAssertString:程序訪問URL,會拿到服務端響應的HTML,然後判斷響應的HTML是否包含這裏設置的斷言,有則證明升級成功;
好,來看代碼:
先看幾個私有的變量:(變量用戶後面的註釋有說明)
        static List<FileInfo> clientFileInfos = new List<FileInfo>();//用於存儲本地待對比的文件
static List<string> IgnorFilePattens = new List<string>();//用於存儲過濾器,過濾器命中的文件不用參與對比
static Dictionary<FileInfo,string> prepareFileInfo = new Dictionary<FileInfo, string>();//用於存儲對比後待上傳的文件
static NameValueCollection setting = ConfigurationManager.AppSettings;
再來看Main函數:(在幾個關鍵點我都寫了註釋)
static void Main(string[] args)
{
Console.WriteLine("開始比對文件(根據文件的修改時間)?y:開始。其他按鍵退出程序:");
if (Console.ReadLine() != "y")
{
return;
}
Console.WriteLine("開始比對文件...");
IgnorFilePattens.AddRange(setting["IgnorFilePatten"].Split(‘;‘));//把過濾器先緩存起來
getClientFileInfos(setting["ClientPath"]);//遞歸取本地文件,過濾器命中的文件跳過
sftpCompareFile(sftpClient => //本地文件與服務器文件對比
{
if (prepareFileInfo.Count < 1)
{
Console.WriteLine("沒有需要更新的文件,按任意鍵退出程序!");
Console.ReadKey();
return;
}
Console.WriteLine("開始停機上傳文件?y:開始。其他按鍵退出程序:");
if (Console.ReadLine() != "y")
{
return;
}
sshStartAndStopWebServer(() => //啟停Web服務器
{
foreach (var fileInfo in prepareFileInfo.Keys)
{
using (var fileStream = fileInfo.OpenRead())
{
sftpClient.BufferSize = 6 * 1024;
sftpClient.UploadFile(fileStream, prepareFileInfo[fileInfo],true); //上傳文件
}
Console.WriteLine("上傳完成:" + prepareFileInfo[fileInfo]);
}
});
Thread.Sleep(918); //留給服務器喘息的時間
Console.WriteLine("開始請求目標網站...");
var html = GetHtml(setting["WebSiteUrl"]); //請求Web的URL
if (html.Contains(setting["WebSiteAssertString"])) //判斷斷言是否命中
{
Console.WriteLine("升級成功,按任意鍵退出程序");
}
else
{
Console.WriteLine("升級失敗,請聯系管理員!按任意鍵退出程序!");
}
Console.ReadKey();
});
}

接下來,我們一點一點的看main函數裏的幾個關鍵點:先看遞歸取本地文件,過濾器命中的文件跳過
        static void getClientFileInfos(string path)
{
var directoryPaths = Directory.GetDirectories(path);
foreach (var directoryPath in directoryPaths)
{
getClientFileInfos(directoryPath); //遞歸,自己調自己
}
var filePaths = Directory.GetFiles(path);
foreach(var filePath in filePaths)
{
var fi = new FileInfo(filePath);
var flag = false;
foreach(var patten in IgnorFilePattens)
{
flag = Regex.IsMatch(fi.Name, patten);
if (flag)
{
break; //有一個過濾器命中,則不用管其他過濾器了
}
}
if (flag)
{
continue; //命中的文件,則跳過
}
clientFileInfos.Add(fi);
}
}
本地文件與服務器文件對比:(按最後一次修改時間對比)
static void sftpCompareFile(Action<SftpClient> actor)
{
using (var client = new SftpClient(setting["ServerIPAddress"], setting["SSHUserName"], setting["SSHPassWord"]))
{
client.Connect();
foreach (var fileInfo in clientFileInfos)
{
var subName = fileInfo.FullName.Remove(0, setting["ClientPath"].Length);
if (subName.StartsWith("\\"))
{
subName = subName.Remove(0, 1);
}
subName = subName.Replace(‘\\‘, ‘/‘);
var serverPath = setting["ServerPath"] + subName;//前面幾行代碼都是為了拿到該文件在服務端的絕對路徑,配置裏的serverPath必須以/結尾,此處不做校驗;
var flag = client.Exists(serverPath);
if (!flag)
{
prepareFileInfo.Add(fileInfo, serverPath); //如果服務端不存在這個文件,則這個文件是肯定要上傳上去的,註意:這裏沒管目錄存在不存在
Console.WriteLine("待上傳文件:" + subName);
}
else
{
var dt = client.GetLastWriteTime(serverPath);
if (dt < fileInfo.LastWriteTime) //文件最後一次更新時間比較,本地的時間比服務端的時間新,則需要上傳
{
prepareFileInfo.Add(fileInfo, serverPath);
Console.WriteLine("待上傳文件:" + subName);
}
}
}
actor(client);
}
}
再來看啟停Web服務器的代碼:(就是直接執行配置文件中的命令,沒什麽特別的)
static void sshStartAndStopWebServer(Action actor)
{
using (var sshclient = new SshClient(setting["ServerIPAddress"], setting["SSHUserName"], setting["SSHPassWord"]))
{
sshclient.Connect();
using (var cmd = sshclient.CreateCommand(setting["HttpServerStopCommand"]))
{
cmd.Execute();
Console.WriteLine("停用HttpServer");
}
actor();
using (var cmd = sshclient.CreateCommand(setting["HttpServerStartCommand"]))
{
cmd.Execute();
Console.WriteLine("啟用HttpServer");
}
sshclient.Disconnect();
}
}
請求HTML的代碼
static string GetHtml(string url)
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Timeout = 16 * 1000;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
string html = reader.ReadToEnd();
stream.Close();
return html;
}

這個項目用到了一個關鍵的庫:SSH.NET在這裏向作者致敬!
感興趣的朋友,也可以加我的QQ群溝通:51021155

ASP.NET CORE Linux發布工具(文件對比 只上傳差異文件;自動啟停WebServer命令;上傳完成自動預熱WebServer)