logo

上次写的文章,关于热更新有点问题。所以重新写了一下。先创建一个txt文件,写入现在的版本号,在打开客户端的时候,检查更新,对比本地的版本号,大于时下载更新,关闭当前软件,同时开启子进程,调用bat脚本,替换原来的asar文件,bat重启客户端。

心情不是很好,我直接上代码吧。

index.js入口文件,也就是运行的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const pack = require('./src/main')

const config = {
width: 1280, // 窗口的宽度
minWidth: 1280, // 窗口最小宽度
height: 960, // 窗口高度
minHeight: 960, // 窗口最小高度
frame: true, // 是否有边框
center: true, // 窗口是否在中心
minimizable: true, //窗口是否可以最小化
maximizable: true, //窗口是否可以最大化
url: './dist/index.html', // 打包地址
isWeb: false, // 是否是网站
openDevTools: false, // 是否打开开发者工具
}

pack(config)

main主文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
'use strict'
const electron = require('electron')
const path = require("path");
const _axios = require('axios')
const update = require('./dowload')
const packages = require('../package.json')
const fs = require('fs')
const util = require("util")

/**
* @param {Object} config
* @example
* config = {
* width: 1280, // 窗口的宽度 number
* minWidth: 1280, // 窗口最小宽度 number
* height: 960, // 窗口高度 number
* minHeight: 960, // 窗口最小高度 number
* frame: true, // 是否有边框 boolean
* center: true, // 窗口是否在中心 boolean
* minimizable: true, //窗口是否可以最小化 boolean
* maximizable: true, //窗口是否可以最大化 boolean
* url:'http://www.zhihu.com' // 打包地址 string
* isWeb: true,// 是否是网站 boolean
* openDevTools: true, // 是否打开开发者工具 boolean
*/

const pack = (config) => {
const {
app,
BrowserWindow,
Menu,
dialog,
} = electron
const dirPath = path.join(__dirname)
const dirPathO = path.join(__dirname).split('resources')
console.log('dirPathO', dirPathO);
const relativePath = dirPathO[0];

let myWindow = null
// 创建窗口
const createWindow = () => {
// 隐藏菜单栏
Menu.setApplicationMenu(null)
// 创建浏览器窗口
const win = new BrowserWindow({
// icon: './dist/login_logo.png', //窗口图标
width: config.width,
minWidth: config.minWidth,
height: config.height,
minHeight: config.minHeight,
// resizable: config.resizable, //禁止改变主窗口尺寸
// fullscreen: config.resizable,//全屏
frame: config.frame, //有边框
// transparent: config.transparent,//透明边框
center: config.center, //窗口是否在中心
minimizable: config.minimizable, //窗口是否可以最小化.
maximizable: config.maximizable, //窗口是否可以最大化.
// kiosk: config.kiosk, //使用kiosk模式。如果使用kiosk模式,应用程序将全屏显示,并且阻止用户离开应用
webPreferences: {
nodeIntegration: false, //是否在Web工作器中启用了Node集成
webSecurity: false,
// preload: __dirname + '/preload.js'
// preload: path.join(app.getAppPath(), 'preload.js'),
},
})
myWindow = win
//窗口默认最大化
win.maximize()
win.show()

// 并且为你的应用加载index.html
if (config.isWeb) {
win.loadURL(config.url)

} else {
win.loadFile(config.url)
}
console.log('version', process.versions.chrome);
// 打开开发者工具
if (config.openDevTools) {
win.webContents.openDevTools()
}
}

//检测是否是只打开了一个窗口程序
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// 当运行第二个实例时,将会聚焦到myWindow这个窗口
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
myWindow.show()
}
})

// 创建 myWindow, 加载应用的其余部分, etc...
app.on('ready', () => {})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('activate', () => {
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
}


// 获取版本号
const versionPath = relativePath + '/version.txt'
const logoPath = relativePath + '/log.txt'

fs.readFile(versionPath, 'utf8', (err, data) => {
if (!err) {
console.log('data', data.toString());
up(data.toString())
} else {
console.log(err);
const localVersion = app.getVersion()
fs.writeFile(versionPath, localVersion, err => {
if (err) {
const error = util.inspect(err, {
depth: null
})
fs.appendFile(path, `错误信息:${error}`, (err) => {
if (err) throw err
console.log(`错误信息已经写入${path}中!`)
})
} else {
console.log('Version save success!');
}
})
}
})
// 热更新检查
const up = (localVersion) => {
_axios({
url: 'http://183.134.197.66:13010/zjg_3s/version',
method: 'get',
params: {
code: '1',
},
}).then(res => {
console.log('res', res.data.errorCode);
// const path = relativePath + '/log.txt'
const resStr = util.inspect(res.data, {
depth: null
})
const packagesStr = util.inspect(packages, {
depth: null
})
fs.appendFile(logoPath, `${resStr},${packagesStr}*****${dirPath},${localVersion}`, (error) => {
if (error) throw error
console.log(`成功已经写入${path}中!`)
})
if (res.data.errorCode == 0) {
console.log('localVersion', localVersion);
const onlineVersion = res.data.data.name
console.log('onlineVersion', onlineVersion);

fs.appendFile(logoPath, `下载更新!`, (error) => {
if (error) throw error
console.log(`成功已经写入${logoPath}中!`)
})
if (localVersion < onlineVersion) {

const dialogOpts = {
type: 'info',
buttons: ['立即更新', '稍后更新'],
title: '更新提醒',
message: `您有新的更新!`,
// detail: `内容如下:` + `${dirPathO},${packages},${localVersion},${onlineVersion}`
detail: `内容如下:` + `${res.data.data.attach}`
}

dialog.showMessageBox(dialogOpts).then((returnValue) => {
console.log('returnValue', returnValue);
if (returnValue.response === 0) {

update(res.data.data.httpPath)
}
})
}
}
})
}


// In this file you can include the rest of your app's specific main process
// code. 也可以拆分成几个文件,然后用 require 导入。
}

module.exports = pack

dowload更新文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//文件下载
const fs = require('fs')
const path = require("path");
const axios = require('axios')
const fsPromises = require('fs').promises
// const package = require('../package.json')
const {
exec,
spawn
} = require('child_process');
const electron = require('electron')

const {
app,
} = electron
var util = require("util")


// 获取真实的绝对路径
const dirPathO = path.join(__dirname).split('resources')
console.log('dirPathO', dirPathO);
const relativePath = dirPathO[0];

console.log('relativePath', relativePath);



// 获取一个绝对路径的文件夹
const dirPath = path.join(relativePath, "/dowload");
console.log('dirPath', dirPath);

// 热更新
const update = (url) => {
fs.access(dirPath, (err) => {
console.log('err', err);
if (err) { //如果文件不存在,就创建这个文件
fs.mkdir(dirPath, (err) => {
console.log(err);
if (!err) {
console.log('dowload file create success!');
dowloadFile(url)
}
});
} else {
//如果这个文件已经存在
dowloadFile(url)
}
})
}

let num = 0
// 文件重命名
const reName = (name, newName, suffix) => {
num++
console.log('begin to rename!');
fs.rename(relativePath + 'dowload\\' + name, relativePath + 'dowload\\' + newName + '.' + suffix, err => {
console.log('error', err);

if (err) {
if (num <= 1) {
console.log('rename again!');
reName(name, newName, suffix)
} else {
console.log('rename failed!', err);
}
} else {
console.log('rename success!');
reviseVersion(name, newName, suffix)
}
})
}

// 调用脚本复制
const shellReName = (name, newName, suffix) => {
let bat, shellPath
if (suffix == 'asar') {
shellPath = path.join(relativePath, "/copy.bat");
console.log('shellPath', shellPath);
} else {
shellPath = path.join(relativePath, "/copyzjg.bat");
console.log('shellPath', shellPath);
}
bat = spawn(shellPath);

bat.stdout.on('data', (data) => {
app.quit()
console.log('data', data);
// copyFile(newName, suffix)
});

bat.stderr.on('data', (data) => {
console.error('error', data);
});

bat.on('exit', (code) => {
console.log(`子进程退出,退出码 ${code}`);
});
}

// 移动文件
const copyFile = (newName, suffix) => {
const copiedPath = relativePath + 'dowload/' + newName + '.' + suffix;
const resultPath = relativePath + 'resources/' + newName + '.' + suffix;
fsPromises.copyFile(copiedPath, resultPath)
.then(() => {
console.log('copyFile success!');
openProgram()
// reviseVersion(name)
// reName(name, newName, suffix)
// shellReName(copiedPath, resultPath, name, newName, suffix)
}).catch((err) => {
console.log('copyFile failed!');
console.log(err);
});
}


// 修改package的version号码
const reviseVersion = (name, newName, suffix) => {

let path = relativePath + '/log.txt'

const versionPath = relativePath + '/version.txt'
fs.writeFile(versionPath, name, err => {
if (err) {
const error = util.inspect(err, {
depth: null
})
fs.appendFile(path, `错误信息:${error}`, (err) => {
if (err) throw err
console.log(`错误信息已经写入${path}中!`)
})
} else {
console.log('Version save success!');
}
})
shellReName(name, newName, suffix)
}

// 打开指定文件程序
const openProgram = () => {
const path = relativePath + '/jixin.exe'
exec(path, (err, data) => {
if (err) {
console.error('exe open failed', err);
return;
}
console.log('exe open success', data.toString());
});
}

// 下载文件
const dowloadFile = (url) => {
console.log('url', url);
// 获取文件名称
// const name = url.split('/').pop().split('_')[0] + '.' + url.split('/').pop().split('_').pop().split('.').pop()
const name = url.split('/').pop().split('_')[0]
console.log(name);
// 获取文件后缀
const suffix = url.split('/').pop().split('_').pop().split('.').pop()
console.log('suffix', suffix);
// const filePath = path.resolve('./src/dowload', name)
// console.log('filePath', filePath);
// return
axios({
method: 'get',
url,
timeout: 10 * 60 * 1000,
maxContentLength: Infinity,
responseType: 'stream',
})
.then(res => {
// console.log(res.data);
console.log('relativePath', relativePath);
let w, newName;
w = fs.createWriteStream(relativePath + '\\dowload\\' + name)

res.data.pipe(w)
return new Promise((resolve, reject) => {
w.on('finish', () => {
console.log('This file is over end!');
if (suffix == 'asar') {
newName = 'app'

// 复制文件
// copyFile(relativePath + 'dowload/' + name, relativePath + 'resources/' + name, name, newName, suffix)
// 调用脚本重命名
// shellReName(relativePath + 'dowload/' + name, relativePath + 'resources/' + name, name, newName, suffix)
// copyFile(relativePath + 'dowload/' + newName + '.' + suffix, relativePath + 'resources/' + newName + '.' + suffix, name)
// 重命名下载的文件
reName(name, newName, suffix)
// w = fs.createWriteStream(relativePath + 'resources/' + name + '.' + suffix)
} else {
newName = 'zjg_2d'

// 复制文件
// copyFile(relativePath + 'dowload/' + name, relativePath + 'static/' + name, name, newName, suffix)
// 调用脚本重命名
// shellReName(relativePath + 'dowload/' + name, relativePath + 'resources/' + name, name, newName, suffix)
// copyFile(relativePath + 'dowload/' + newName + '.' + suffix, relativePath + 'static/' + newName + '.' + suffix, name)
// 重命名下载的文件
reName(name, newName, suffix)
// w = fs.createWriteStream(relativePath + 'static/' + name + '.' + suffix)
}
resolve()
})
w.on('error', (err) => {
console.log('error', err);
reject()
})
})
})
}


module.exports = update

copy脚本文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@echo off

set filePath=%~dp0
echo %filePath%
cd %filePath%
%~d0

cd %filePath%dowload

copy %filePath%dowload\app.asar %filePath%resources
echo 'copy success'

echo 'open exe'
cd %filePath%
start jixin.exe

这是一个完整的思路,并且经过我实践过了,测试了很久,暂无问题的方案。
我是一个菜鸡程序员,勿喷。有更好的想法,可以互相交流