Commit fc5d15e5 authored by 罗显健's avatar 罗显健

[13617]打包工具优化--增加合并sql

parent ba337e9e
using System.IO;
using System.Text;
namespace Ftt.PatchPack
{
public class EncodingHelper
{
#region 文件编码相关
/// <summary>
/// 获取文件的编码格式
/// </summary>
public static Encoding GetEncoding(string fileName)
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
Encoding r = GetEncoding(fs);
fs.Close();
return r;
}
}
/// <summary>
/// 通过给定的文件流,判断文件的编码类型
/// </summary>
/// <param name="fs">文件流</param>
/// <returns>文件的编码类型</returns>
public static Encoding GetEncoding(FileStream fs)
{
//文件的字符集在Windows下有两种,一种是ANSI,一种Unicode。
//对于Unicode,Windows支持了它的三种编码方式,一种是小尾编码(Unicode),一种是大尾编码(BigEndianUnicode),一种是UTF - 8编码。
//byte[] Unicode = new byte[] { 0xFF, 0xFE };
//byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF };
//byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //BOM头
if (fs.Length < 3)
return Encoding.Default;
byte[] bytes = new byte[3];
fs.Read(bytes, 0, 3);
Encoding reVal = Encoding.GetEncoding("GB2312");
if (bytes[0] == 0xFE && bytes[1] == 0xFF)
{
reVal = Encoding.BigEndianUnicode;
}
else if (bytes[0] == 0xFF && bytes[1] == 0xFE)
{
reVal = Encoding.Unicode;
}
else
{
if (!(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF))
{
fs.Position = 0;
}
if (IsUTF8Bytes(fs))
{
if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
reVal = new UTF8Encoding(false);
else
reVal = Encoding.UTF8;
}
}
return reVal;
}
private static byte UTF8_BYTE_MASK = 0b1100_0000;
private static byte UTF8_BYTE_VALID = 0b1000_0000;
private static bool IsUTF8Bytes(FileStream fs)
{
//BinaryReader r = new BinaryReader(fs);
byte[] bytes = new byte[1];
fs.Read(bytes, 0, 1);
//1字节 0xxxxxxx
//2字节 110xxxxx 10xxxxxx
//3字节 1110xxxx 10xxxxxx 10xxxxxx
//4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
//5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
//6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
while (fs.Read(bytes, 0, 1) > 0)
{
if (bytes[0] < 0x80)
continue;
int cnt = 0;
byte b = bytes[0];
while ((b & 0b1000_0000) != 0)
{
cnt++;
b <<= 1;
}
cnt -= 1;
for (int i = 0; i < cnt; i++)
{
if (fs.Read(bytes, 0, 1) <= 0)
return false;
if ((bytes[0] & UTF8_BYTE_MASK) != UTF8_BYTE_VALID)
return false;
}
}
return true;
}
#endregion
/// <summary>
/// 读取文件内容
/// </summary>
/// <param name="filePath"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string ReadAllText(string filePath, out Encoding encoding)
{
encoding = EncodingHelper.GetEncoding(filePath);
return File.ReadAllText(filePath, encoding);
}
}
}
......@@ -74,14 +74,16 @@ namespace Ftt.PatchPack
res = res && Compresion();
res = res && DeleteDirectory(mPackDirectory);
return res;
}
catch (Exception ex)
{
throw ex;
}
finally
{
ProjectHelp.DeleteDirectoryIfExists(mPackDirectory);
}
}
#region 私有方法
......@@ -98,7 +100,7 @@ namespace Ftt.PatchPack
try
{
DeleteDirectory(path);
ProjectHelp.DeleteDirectoryIfExists(path);
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
}
......@@ -167,22 +169,7 @@ namespace Ftt.PatchPack
return true;
}
/// <summary>
/// 删除目录,并删除该目录中所有子目录和文件
/// </summary>
private bool DeleteDirectory(string path)
{
try
{
if (Directory.Exists(path)) Directory.Delete(path, true);
return true;
}
catch (Exception ex)
{
throw new Exception($"删除文件夹失败:{ex.Message}");
}
}
/// <summary>
/// 删除该包同名的历史包(名称相同,日期不同)
......
......@@ -42,6 +42,7 @@
this.pnlLeft = new System.Windows.Forms.Panel();
this.gpbLog = new System.Windows.Forms.GroupBox();
this.txtLog = new System.Windows.Forms.TextBox();
this.ckbMergeSql = new System.Windows.Forms.CheckBox();
this.pnlTop.SuspendLayout();
this.pnlLeft.SuspendLayout();
this.gpbLog.SuspendLayout();
......@@ -99,7 +100,7 @@
this.lstProject.RightToLeft = System.Windows.Forms.RightToLeft.No;
this.lstProject.RightToLeftLayout = true;
this.lstProject.ShowGroups = false;
this.lstProject.Size = new System.Drawing.Size(573, 231);
this.lstProject.Size = new System.Drawing.Size(672, 231);
this.lstProject.TabIndex = 10;
this.lstProject.UseCompatibleStateImageBehavior = false;
this.lstProject.View = System.Windows.Forms.View.List;
......@@ -111,12 +112,13 @@
this.lblNotes.Location = new System.Drawing.Point(0, 289);
this.lblNotes.Name = "lblNotes";
this.lblNotes.Padding = new System.Windows.Forms.Padding(12, 8, 0, 0);
this.lblNotes.Size = new System.Drawing.Size(573, 52);
this.lblNotes.Size = new System.Drawing.Size(672, 52);
this.lblNotes.TabIndex = 11;
this.lblNotes.Text = "注意:本工具只打包项目设定文件夹内通用文件。。。。。详细内容见源代码";
//
// pnlTop
//
this.pnlTop.Controls.Add(this.ckbMergeSql);
this.pnlTop.Controls.Add(this.ckbShowLog);
this.pnlTop.Controls.Add(this.ckbDelHisPack);
this.pnlTop.Controls.Add(this.btnOpenDirectory);
......@@ -128,7 +130,7 @@
this.pnlTop.Location = new System.Drawing.Point(0, 0);
this.pnlTop.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.pnlTop.Name = "pnlTop";
this.pnlTop.Size = new System.Drawing.Size(573, 58);
this.pnlTop.Size = new System.Drawing.Size(672, 58);
this.pnlTop.TabIndex = 12;
//
// ckbShowLog
......@@ -192,16 +194,16 @@
this.pnlLeft.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnlLeft.Location = new System.Drawing.Point(0, 0);
this.pnlLeft.Name = "pnlLeft";
this.pnlLeft.Size = new System.Drawing.Size(573, 341);
this.pnlLeft.Size = new System.Drawing.Size(672, 341);
this.pnlLeft.TabIndex = 13;
//
// gpbLog
//
this.gpbLog.Controls.Add(this.txtLog);
this.gpbLog.Dock = System.Windows.Forms.DockStyle.Right;
this.gpbLog.Location = new System.Drawing.Point(573, 0);
this.gpbLog.Location = new System.Drawing.Point(672, 0);
this.gpbLog.Name = "gpbLog";
this.gpbLog.Size = new System.Drawing.Size(360, 341);
this.gpbLog.Size = new System.Drawing.Size(352, 341);
this.gpbLog.TabIndex = 0;
this.gpbLog.TabStop = false;
this.gpbLog.Text = "************** 操作日志 **************";
......@@ -212,14 +214,26 @@
this.txtLog.Location = new System.Drawing.Point(3, 19);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.Size = new System.Drawing.Size(354, 319);
this.txtLog.Size = new System.Drawing.Size(346, 319);
this.txtLog.TabIndex = 0;
//
// ckbMergeSql
//
this.ckbMergeSql.AutoSize = true;
this.ckbMergeSql.BackColor = System.Drawing.SystemColors.Control;
this.ckbMergeSql.ForeColor = System.Drawing.Color.Blue;
this.ckbMergeSql.Location = new System.Drawing.Point(573, 33);
this.ckbMergeSql.Name = "ckbMergeSql";
this.ckbMergeSql.Size = new System.Drawing.Size(93, 21);
this.ckbMergeSql.TabIndex = 14;
this.ckbMergeSql.Text = "合并Sql脚本";
this.ckbMergeSql.UseVisualStyleBackColor = false;
//
// FrmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(933, 341);
this.ClientSize = new System.Drawing.Size(1024, 341);
this.Controls.Add(this.pnlLeft);
this.Controls.Add(this.gpbLog);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
......@@ -255,6 +269,7 @@
private System.Windows.Forms.CheckBox ckbShowLog;
private System.Windows.Forms.GroupBox gpbLog;
private System.Windows.Forms.TextBox txtLog;
private System.Windows.Forms.CheckBox ckbMergeSql;
}
}
......@@ -9,9 +9,6 @@ namespace Ftt.PatchPack
{
public partial class FrmMain : Form
{
private const int WIDTH_LOG_AREA = 360;
Dictionary<string, Project> projects = new Dictionary<string, Project>();
/// <summary>
......@@ -30,6 +27,7 @@ namespace Ftt.PatchPack
lblNotes.Text = builder.ToString();
new ToolTip().SetToolTip(ckbDelHisPack, "勾选后、可自动清理对应包的历史包文件");
new ToolTip().SetToolTip(ckbMergeSql, "合并未使用Use XX指令的脚本,Use指令的脚本文件独立存在并间隔合并操作。");
}
......@@ -41,7 +39,6 @@ namespace Ftt.PatchPack
private void InitForm()
{
gpbLog.Width = WIDTH_LOG_AREA;
LogVisible(false);
projects = ProjectHelp.Read();
......@@ -124,6 +121,7 @@ namespace Ftt.PatchPack
int success = 0;
Cursor.Current = Cursors.WaitCursor;
Project.MergeSqlFile = ckbMergeSql.Checked;
foreach (ListViewItem item in items)
{
......@@ -149,6 +147,7 @@ namespace Ftt.PatchPack
}
finally
{
ProjectHelp.DeleteDirectoryIfExists(project.MergeSqlTempFullPath);
Application.DoEvents();
}
}
......
......@@ -70,6 +70,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="EncodingHelper.cs" />
<Compile Include="FilePatch.cs" />
<Compile Include="FrmMain.cs">
<SubType>Form</SubType>
......
......@@ -2,7 +2,10 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace Ftt.PatchPack
......@@ -16,13 +19,11 @@ namespace Ftt.PatchPack
/// </summary>
private string mExeName;
/// <summary>
/// 项目名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// Exe文件名称
/// </summary>
......@@ -37,24 +38,35 @@ namespace Ftt.PatchPack
set { mExeName = value; }
}
/// <summary>
/// 根目录绝对路径
/// </summary>
public string AbsoluteRootDirectory { get; set; }
/// <summary>
/// 应用文件相对路径,相对于根目录。
/// </summary>
public string RelativeAppFilePath { get; set; } = "ExportPath";
/// <summary>
/// 数据库文件相对路径,相对于根目录。
/// </summary>
public string RelativeDbFilePath { get; set; } = "开发文档\\数据库脚本";
public string RelativeDbFilePath { get; set; } = @"开发文档\数据库脚本";
/// <summary>
/// 是否合并sql文件.
/// </summary>
public static bool MergeSqlFile = false;
/// <summary>
/// 合并Sql文件的临时目录,在<see cref="RelativeDbFilePath"/>目录下。
/// </summary>
public string MergeSqlTempFullPath { get; set; }
/// <summary>
/// 合并Sql的相对路径。
/// </summary>
static string MergeSqlRelativePath = "MergeTemp";
#endregion
......@@ -66,11 +78,8 @@ namespace Ftt.PatchPack
static int PatchTimeFrame_Dll = 48;
static int PatchTimeFrame_Sql = 720;
static int PatchTimeFrame_Xml = 720;
static bool Exclude_PDB = false;
static Project()
{
//检查配置文件
......@@ -112,11 +121,8 @@ namespace Ftt.PatchPack
if (_exclude_PDB == 1) Exclude_PDB = true;
}
#region 公共方法
/// <summary>
/// 返回所有补丁文件列表,
/// DB + App
......@@ -135,8 +141,6 @@ namespace Ftt.PatchPack
return validFiles;
}
/// <summary>
/// 返回应用程序补丁文件列表,
/// dll + pdb + xml
......@@ -210,10 +214,9 @@ namespace Ftt.PatchPack
}
}
return valid;
return MergeSqlFile ? MergeSql(valid) : valid;
}
/// <summary>
/// 获取据库脚本补丁,返回有效的文件夹,一个月以内
/// </summary>
......@@ -242,13 +245,56 @@ namespace Ftt.PatchPack
return validPath;
}
/// <summary>
/// 合并sql。
/// <para>1、逐个判断,是否包含显示的Use XXX,如果有,则该文件不参与合并,且中段前后文件的合并。</para>
/// <para>2、如果没有,则作为可合并的文件,合并到上一个文件或等待其他文件并入。</para>
/// </summary>
/// <param name="sqlFiles">待合并的sql文件</param>
/// <returns></returns>
private List<PatchFileInfo> MergeSql(List<PatchFileInfo> sqlFiles)
{
List<PatchFileInfo> mergeSqlFiles = new List<PatchFileInfo>();
#endregion
MergeSqlTempFullPath = Path.Combine(AbsoluteRootDirectory, RelativeDbFilePath, MergeSqlRelativePath);
ProjectHelp.DeleteDirectoryIfExists(MergeSqlTempFullPath);
Directory.CreateDirectory(Path.Combine(MergeSqlTempFullPath, "DB"));//为了与源路径保持结构一致,没有日期部份。
FileInfo destMergeFile = null;//合并文件锚点
foreach (var sourceFile in sqlFiles)
{
var tmpFile = Path.Combine(MergeSqlTempFullPath, $"{sourceFile.RelativePatchPath}_{sourceFile.Name}");
File.Copy(sourceFile.FullName, tmpFile, true);
#region 私有方法
if (ProjectHelp.CanMergeSqlFile(tmpFile))
{
if (destMergeFile == null) //标记锚点、该文件作为合并目标文件。需要打包。
{
destMergeFile = new FileInfo(tmpFile);
}
else
{
//合并并删除被合并的文件,且无需打包被合并的文件。
if (ProjectHelp.MergeFileContent(tmpFile, destMergeFile.FullName, true, true)) continue;
}
}
else
{ //如果不能合并,则需要重置合并锚点(无论是否存在)
destMergeFile = null;
}
//需要被打包的文件信息。合并过成功中删减了文件父级“日期”的部份。这里补回去一个当前日期的层级。
var mergeFile = new PatchFileInfo(new FileInfo(tmpFile).Name, tmpFile, $@"DB\{DateTime.Now.ToString("yyyy-MM-dd")}");
mergeSqlFiles.Add(mergeFile);
}
return mergeSqlFiles;
}
#endregion
#region 私有方法
/// <summary>
/// 获取有效的打包文件列表
......@@ -267,7 +313,6 @@ namespace Ftt.PatchPack
return GetValidPatchFile(files, oldPath, newPath, hours);
}
/// <summary>
/// 获取有效的打包文件列表
/// </summary>
......@@ -305,7 +350,6 @@ namespace Ftt.PatchPack
return valid;
}
/// <summary>
/// 判断给定时间是否在有效期内。
/// </summary>
......@@ -318,7 +362,6 @@ namespace Ftt.PatchPack
return dateTime.AddHours(hours) >= DateTime.Now;
}
/// <summary>
/// 返回过去一个月的时长,单位秒
/// </summary>
......@@ -351,25 +394,22 @@ namespace Ftt.PatchPack
#endregion
}
/// <summary>
/// 帮助类
/// </summary>
public class ProjectHelp
{
static private string fileName = "project.json";
static private Encoding encoding = Encoding.Default;
static string FilePath { get { return Path.Combine(Application.StartupPath, fileName); } }
public static Dictionary<string, Project> Read()
{
Dictionary<string, Project> keyValues = new Dictionary<string, Project>();
if (!System.IO.File.Exists(FilePath)) return keyValues;
try
{
keyValues = JsonConvert.DeserializeObject<Dictionary<string, Project>>(System.IO.File.ReadAllText(FilePath, System.Text.Encoding.Default));
keyValues = JsonConvert.DeserializeObject<Dictionary<string, Project>>(System.IO.File.ReadAllText(FilePath, encoding));
}
catch (Exception)
{
......@@ -378,7 +418,6 @@ namespace Ftt.PatchPack
return keyValues;
}
/// <summary>
/// 序列化保存
/// </summary>
......@@ -388,7 +427,7 @@ namespace Ftt.PatchPack
{
try
{
System.IO.File.WriteAllText(FilePath, JsonConvert.SerializeObject(dicClient), System.Text.Encoding.Default);
System.IO.File.WriteAllText(FilePath, JsonConvert.SerializeObject(dicClient), encoding);
}
catch (Exception)
{
......@@ -396,6 +435,65 @@ namespace Ftt.PatchPack
}
return true;
}
}
/// <summary>
/// 删除目录(如果存在),并删除该目录中所有子目录和文件
/// </summary>
public static bool DeleteDirectoryIfExists(string path)
{
try
{
if (Directory.Exists(path))
Directory.Delete(path, true);
return true;
}
catch (Exception ex)
{
throw new Exception($"删除文件夹失败:{ex.Message}");
}
}
/// <summary>
/// 判断Sql文件是否能被合并
/// <para>原则:有使用Use 指令的文件禁止合并</para>
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static bool CanMergeSqlFile(string filePath)
{
if (!File.Exists(filePath)) return false;
var encoding = EncodingHelper.GetEncoding(filePath);
var sqlScript = File.ReadAllText(filePath, encoding);
// 使用正则表达式进行匹配,判断是否包含Use DbName的显示声明
string pattern = @"\bUse\s+\w+";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(sqlScript);
return !match.Success;
}
/// <summary>
/// 合并文件,把源文件的内容合并到目标文件中
/// </summary>
/// <param name="sourceFileName">待合并的源文件</param>
/// <param name="destFileName">目标的目标文件</param>
/// <param name="appendToNewLine">文件内容是否追加到新行</param>
/// <param name="delAfterMerge">合并成功后是否删除被合并的源文件</param>
public static bool MergeFileContent(string sourceFileName, string destFileName, bool appendToNewLine = true, bool delAfterMerge = true)
{
var content = EncodingHelper.ReadAllText(sourceFileName, out Encoding _);
using (StreamWriter writer = new StreamWriter(destFileName, true, EncodingHelper.GetEncoding(destFileName)))
{
if (appendToNewLine) writer.WriteLine("");
writer.Write(content);
}
if (delAfterMerge) File.Delete(sourceFileName); //合并成功,把源文件删除。
return true;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment