经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
基于DotNetty实现自动发布 - 实现一键打包发布
来源:cnblogs  作者:Broadm  时间:2023/12/13 9:11:24  对本文有异议

前言

上一篇,我只实现了一键检测代码变化,本篇才是真正的实现了一键打包发布

效果图

image
image
image

客户端打包待发布文件

  1. /// <summary>
  2. /// 把多个文件添加到压缩包 (保留文件夹层级关系)
  3. /// </summary>
  4. public static async Task<ZipFileResult> CreateZipAsync(IEnumerable<ZipFileInfo> zipFileInfo)
  5. {
  6. return await Task.Run(() =>
  7. {
  8. var zipDir = EnsureZipDirCreated();
  9. var zipFileName = $"{DateTime.Now:yyyyMMdd_HHmmss_}{Guid.NewGuid()}.zip";
  10. var zipPath = Path.Combine(zipDir, zipFileName);
  11. using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);
  12. foreach (var item in zipFileInfo)
  13. {
  14. archive.CreateEntryFromFile(item.FileAbsolutePath, item.FileRelativePath, CompressionLevel.SmallestSize);
  15. }
  16. return new ZipFileResult() { FullFileName = zipPath, FileName = zipFileName };
  17. });
  18. }

客户端封装 NettyMessage

  1. //读取zip字节数组,填充到 NettyMessage 的 Body
  2. var body = await File.ReadAllBytesAsync(zipResult.FullFileName);
  3. //NettyHeader
  4. var header = new DeployRequestHeader()
  5. {
  6. Files = PublishFiles,
  7. SolutionName = SolutionName,
  8. ProjectName = webProject!.ProjectName,
  9. ZipFileName = zipResult.FileName,
  10. };
  11. var nettyMessage = new NettyMessage { Header = header, Body = body };
  12. //创建 NettyClient
  13. Logger.Info("开始发送");
  14. using var nettyClient = new NettyClient(webProject.ServerIp, webProject.ServerPort);
  15. await nettyClient.SendAsync(nettyMessage);
  16. Logger.Info("完成发送");
  17. Growl.SuccessGlobal($"发布成功");
  18. //保存发布记录
  19. await solutionRepo.SaveFirstPublishAsync(SolutionId, SolutionName, lastGitCommit!.Sha);
  20. Growl.SuccessGlobal($"操作成功");
  21. quickDeployDialog?.Close();

NettyHeader 设计

具体实现是 DeployRequestHeader, 继承自 NettyHeader, 保存待发布文件集合,项目名称,解决方案名称, zip 文件名称等

  1. /// <summary>
  2. /// 发布请求头部
  3. /// </summary>
  4. public class DeployRequestHeader : NettyHeader
  5. {
  6. public DeployRequestHeader() : base("Deploy/Run") { }
  7. public List<DeployFileInfo> Files { get; set; } = [];
  8. public string ProjectName { get; set; } = string.Empty;
  9. public string SolutionName { get; set; } = string.Empty;
  10. public string ZipFileName { get; set; } = string.Empty;
  11. }

服务端处理

  • 解压 zip
  • 备份目标文件(存在才备份)
  • 替换目标文件(不存在则新建)
  1. /// <summary>
  2. /// 执行服务端发布
  3. /// </summary>
  4. /// <param name="model"></param>
  5. public void Run(DeployRequestHeader model)
  6. {
  7. Logger.Warn($"收到客户端的消息: {model.ToJsonString(true)}");
  8. var configs = NettyServer.AppHost.Services.GetRequiredService<IOptions<List<ProjectConfig>>>();
  9. var projectConfig = configs.Value.FirstOrDefault(a => a.ProjectName == model.ProjectName);
  10. if (projectConfig == null)
  11. {
  12. Logger.Error("请现在服务器项目的appsettings.json中配置项目信息");
  13. return;
  14. }
  15. var zipBytes = Request.Body;
  16. if (zipBytes == null || zipBytes.Length == 0)
  17. {
  18. Logger.Error("ZipBytes为空");
  19. return;
  20. }
  21. var zipFileName = model.ZipFileName;
  22. if (string.IsNullOrEmpty(zipFileName))
  23. {
  24. Logger.Error("ZipFileName为空");
  25. return;
  26. }
  27. //解压
  28. var zipDir = ZipHelper.UnZip(zipBytes, zipFileName);
  29. Logger.Info($"解压成功: {zipDir}");
  30. //备份并覆盖旧文件
  31. DoPublish(model.Files, zipDir, zipFileName, projectConfig);
  32. Logger.Info($"发布成功: {zipDir}");
  33. }
  1. /// <summary>
  2. /// 备份并覆盖旧文件
  3. /// </summary>
  4. private static void DoPublish(List<DeployFileInfo> files, string zipDir, string zipFileName, ProjectConfig projectConfig)
  5. {
  6. try
  7. {
  8. //先创建备份文件夹
  9. var backupDir = EnsureBackupDirCreated(zipFileName);
  10. //遍历每个待发布的文件,依次先备份再替换
  11. foreach (DeployFileInfo file in files)
  12. {
  13. //文件相对路径(相对于待发布的项目根目录,也是相对于解压后的根目录)
  14. var relativeFilePath = file.PublishFileRelativePath;
  15. //源文件路径(解压后的文件路径)
  16. var sourceFileName = Path.Combine(zipDir, relativeFilePath);
  17. //待发布的文件路径 (服务器真实文件路径)
  18. var destFileName = Path.Combine(projectConfig.ProjectDir, relativeFilePath);
  19. //服务器已存在此文件,先执行备份
  20. if (File.Exists(destFileName))
  21. {
  22. //备份文件路径
  23. var backupFileName = Path.Combine(backupDir, relativeFilePath);
  24. //确保创建备份文件夹
  25. var backupFileDir = Path.GetDirectoryName(backupFileName);
  26. if (!Directory.Exists(backupFileDir))
  27. {
  28. Directory.CreateDirectory(backupFileDir!);
  29. }
  30. File.Copy(destFileName, backupFileName);
  31. }
  32. else
  33. {
  34. //服务器不存在此文件,先创建文件夹层级(比如你新加了一个页面demo.aspx,需要发布到服务器对应的位置)
  35. var destFileDir = Path.GetDirectoryName(destFileName);
  36. if (!Directory.Exists(destFileDir))
  37. {
  38. Directory.CreateDirectory(destFileDir!);
  39. }
  40. }
  41. //替换服务器文件
  42. File.Copy(sourceFileName, destFileName, true);
  43. }
  44. }
  45. catch (Exception ex)
  46. {
  47. Logger.Error(ex.ToString());
  48. }
  49. }

总结

至此,我已经完成了自动发布项目的主体功能,实现自动检测代码变化,自动一键打包发布, 不足的地方有: 第一次发布需要手动处理, 项目也需要手动编译,并配置输出目录,但是我相信,这些都不是问题,只要有思路,都是可以解决的,我主要分享下我的实现步骤

注意

1. 本项目目前只支持 .net framework 的单体 Web 项目
2. 客户端和服务端均是 Windows 服务器
3. 线上项目是 IIS 部署的
4. 代码可能存在 BUG,大家发现可以自行解决,或者联系我,我后面不准备继续维护这个项目,毕竟主要是学习分享用的~~~

代码仓库

项目暂且就叫 OpenDeploy

欢迎大家拍砖,Star

下一步

服务端目前是控制台实现, 可以部署为 Windows 服务, 这个也很简单, 我就不发了, 大家自行实现吧, 完结~

原文链接:https://www.cnblogs.com/broadm/p/17894063.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号