nwjs作为跨平台应用的热更新exe详细文档

1 篇文章 0 订阅
1 篇文章 0 订阅

1.使用到的nodejs模块

① fs模块,对要进行热更新的文件进行处理
② request模块 进行网络请求,获取热更新包
③ adm-zip-iconv模块 解压文件
④ crypto模块 对热更新包进行MD5检测,判断完整性
⑤ ini 模块 读取ini文件

备注:ini和adm-zip-iconv,request模块需要自己npm inistall ,其他两个模块nwjs都会自带

2.新建一个nodejs项目

① 新建一个空文件夹,在空文件夹下启动cmd窗口 运行

npm init -y

文件夹会生成一个packag.json,配置和修改json文件,添加nwjs打开窗口设置

{
  "name": "hotUp",//该名称不要跟你项目的package.json中的name同名
  "main": "updateExe.html",//等下要启动的热更新页面
  "window": {
    "title": "基于nwjs的热更新exe",
    "icon": "static/icon.png",//给启动的热更新页面设置图标
    "toolbar": false,//去除nwjs自身的顶部选项卡
    "frame": false,//使用nwjs的无框模式
    "width": 770,
    "height": 320,
    "max_height": 320,
    "max_width": 770,
    "min_height": 320,
    "min_width": 770,
    "position": "center",
    "transparent": true//透明化窗口
  }
}

以上json的备注内容,为方便阅读才写上去,实际当中,不应该存在,否则启动时会报错

② 新建一个html页面,取名为上面json中main的名称,内容如下

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
    <style>
      body{
        margin:0;
        padding:0;
        background-color: transparent;
      }
      body::-webkit-scrollbar{
        display:none;
      }
      .app{
        background-color: transparent;
        padding:5px;
        margin:auto;
        width: 750px;
        height: 300px;
      }
	  .main{
		  margin:20px auto;
		  text-align: center;
	  }

    </style>
	</head>
	<body>
    <div class="app">
		<div class="main">更新中,请稍后......</div>
    </div>
	<script type="text/javascript">
      var fs = require('fs');
      var request = require('request');
      var path = require('path');
      var AdmZip = require('adm-zip-iconv');
      const crypto = require('crypto');
      var ini = require('ini');
	  
	  var win = nw.Window.get()
	  win.hide();//默认热更新文件页面隐藏,当有热更新时才显示
	  
      //基础文件夹路径
      //const __Tempdirname = path.resolve('./');//在与nwjs打包成exe后,这种方法获取到的是系统的临时目录
      const __dirname = path.dirname(process.execPath)//获取打包后exe的当前文件夹路径

      //打开其他的exe
      var exec = require('child_process').execFile;
      var fun =function(){
         win.hide();
         //要启动的exe程序
         exec(path.join(__dirname ,'index.exe'), function(err, data) {
              console.log("关闭当前exe")
              win.close();
          });
      }

      //检查版本的路径
      const checkUrl = "/checkVersion/?";
      //获取热更新包的路径
      const updateUrl = "/updateVersion/?";
      //临时文件夹名称
      let dirname = "temp";

      var nowVersion = false;//版本号
      var baseURL = false;//服务器名称

      //删除文件或文件夹(reservePath和path一致时,保留最外层文件夹)
      function delFile(path, reservePath="") {
          if (fs.existsSync(path)) {
              if (fs.statSync(path).isDirectory()) {//是文件夹
                  let files = fs.readdirSync(path);//读取文件夹内所有文件,并且遍历
                  files.forEach((file, index) => {
                      let currentPath = path + "/" + file;
                      if (fs.statSync(currentPath).isDirectory()) {//是文件夹递归
                          delFile(currentPath, reservePath);
                      }else{
                          console.log("删除文件")
                          fs.unlinkSync(currentPath);//删除文件
                      }
                  });
                  if (path != reservePath) {
                      console.log(path)
                      console.log("删除temp")
                      fs.rmdirSync(path);
                  }
              } else {//是文件
                  fs.unlinkSync(path);
              }
          }
      }

      hasNew();//检查是否有更新包
      //检查是否有新更新包
      function hasNew(){
        fs.readFile(path.join(__dirname,"setting.ini"),'utf8',function(err,data){
          if(err){
			console.log("缺少配置文件")
            win.show();
            return;
          }
          let parseData = ini.parse(data);
           //获取本地版本
          nowVersion = parseData["version_info"].version;//当前项目exe的版本号
          baseURL = parseData["version_info"].baseURL;//后台地址
          if(!nowVersion || !baseURL){
              console.log(parseData)
              win.show();
              return;

          }else{
              //获取是否有新版本号
              request.get(baseURL + checkUrl + 'version='+ nowVersion,function(err,response,body){
                  if(err){
                    console.log("网络异常")
                    win.show();
                    return;
                  }
                  var bodyInfo = JSON.parse(response.body)
                  if(bodyInfo.code == 200){

                        //有新版本可以热更新
                         if(bodyInfo.version != nowVersion){
                             win.show();
                            //开始更新
                             connetNet();
                         }
						
                         if(bodyInfo.version == nowVersion){
                             fun();//启动index.app
                         }
                  }else{
                    console.log("返回不是200")
                    win.show();
                    return;
          
                  }
              })
           }
        })
      }

       //更新结束
       function updateEnd(msg, finish=false){
		 console.log(msg);
         if(finish){
            //在此检查是否更新
            hasNew();
            //fun();//启动index.app
         }
       }

	
		//开始更新
       function connetNet(){
		  //判断当前路径下,是否有同名临时文件夹
          fs.access(path.join(__dirname,dirname), fs.constants.F_OK, (err) => {
                if(!err){
                  dirname = "temp"+ Date.parse(new Date());
                }
                //先创建一个临时文件夹temp
                fs.mkdir(path.join(__dirname,dirname), { recursive: true }, (err) => {
                      if (err) {
						updateEnd("创建临时文件夹失败")
                        return;
                      };
                
                      //下载文件(发送当前版本给后台)
                      request.get(baseURL + updateUrl + 'version='+ nowVersion,function(err,response,body){
                          if(err){
                            updateEnd("网络异常,请检查网络")
                            return;
                          }
						  //返回,热更新包路径,和热更新包MD5
                          var bodyInfo = JSON.parse(response.body)

                          if(bodyInfo.code == 200){
                            
							//获取热更新包,并且在本地临时文件夹内存储为latest.zip(后台返回的也必须是一个热更新包)
                            request.get(baseURL + bodyInfo.downUrl).pipe(fs.createWriteStream(path.join(__dirname,dirname,"latest.zip")).on("close",function(){
                                console.log("文件下载结束")
                                //检查文件完整性
                                var stream = fs.createReadStream(path.join(__dirname,dirname,"latest.zip"))
                                var fsHash = crypto.createHash('md5');
                                stream.on('data',function(d){
                                  fsHash.update(d)
                                })
                                stream.on("end",function(){
                                    var md5v = fsHash.digest("hex");
									//判断后台返回的zip文件的MD5值和临时文件夹内的latest.zip的MD5值是否一致
                                    if(bodyInfo.md5Value && md5v != bodyInfo.md5Value){
                                          console.log("md5不一样");
                                          updateEnd("压缩包不完整")
                                          //删除临时文件夹
                                          delFile(path.join(__dirname,dirname));
                                    }else{
                                          console.log("开始替换");

                                          //删除原来文件
                                          delFile(path.join(__dirname,"index.exe"))//项目exe
                                          delFile(path.join(__dirname,"setting.ini"))//这个文件夹内保存,当前版本号,和后台地址
                                          //替换文件
                                          var zip = new AdmZip(path.join(__dirname,dirname,"latest.zip"), "GBK");
                                          zip.extractAllTo(__dirname, true)
                
                                          //删除临时文件夹
                                          delFile(path.join(__dirname,dirname));

                                          updateEnd("更新完成",true)
                                    }
                                })
                
                              })).on('error', function(err) {
                                updateEnd("网络异常,请检查网络")
                                return;
                              })
                          }else{
                              //删除临时文件夹
                              delFile(path.join(__dirname,dirname));
                              updateEnd("更新失败")
                          }
                
                      })
                
                });
          });
       }

		</script>
	</body>
</html>

③ 下载所需模块

npm install ini

npm install request

npm install adm-zip-iconv

3.打包

① 选中项目文件夹内的所有内容,打包成app.zip(一定得是zip压缩包)
② 将app.zip修改成app.nw,并且拖到nwjs的目录中
③ 在nwjs的目录中启动cmd,并运行

copy /b nw.exe+app.nw app.exe

4.建立setting.ini文件,并且放到nwjs目录中

[version_info]
version=1.0.0
baseURL=http://www.xxxxxxx.com

baseURL为后台地址,version是项目exe的版本号

5.修改app.exe的使用权限

① 使用ResourceHacker.exe打开app.exe,找到如下文件的位置,修改权限为requireAdministrator
在这里插入图片描述

替换后,点击上面的三角形运行,并ctrl+s 保存,会重新生成app.exe

6.最后的目录结构如图

在这里插入图片描述

7.使用Inno Setup Compiler将以上内容打包成安装包exe

安装包exe通第5步。更改请求权限

以上所有内容就完成了一个带有热更新的项目。自己的项目在index.exe中,有新版本的时候,将新的index.exe和setting.ini打包成zip文件放在服务器,并在后台修改检测版本时,返回最新版本号。当app.exe检测到有新更新包时,会下载更新包文件,并替换掉原来的index.exe和setting.ini。当没有新更新包时,不会显示,会启动index.exe。
后期会将后台部分补上。有问题可留言,记得点个赞,谢谢!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值