electron-updater使用指南
基础
检测是否最新版
autoUpdater.checkForUpdates()
下载最新版
autoUpdater.downloadUpdate()
项目使用
update.js
const { ipcMain } = require('electron')
const { autoUpdater } = require('electron-updater')
const path = require("path")
// 更新地址,该地址下放的是安装包和latest.yml
const updateURL = 'https://jkcgy.obs.cn-south-1.myhuaweicloud.com/desktop/'const message = {error: '软件更新异常,请重试',checking: '正在检查更新',updateAva: '检测到新版本,准备下载',updateDown: '软件下载中,请耐心等待',updateSet: '下载完成,准备安装',updateNotAva: '已经是最新版本',
}//软件版本更新
ipcMain.handle('on-soft-update', (e) => {autoUpdater.checkForUpdates()
})
ipcMain.on("updateDesktop", () => {console.log("checkForUpdates");autoUpdater.checkForUpdates()// console.log("downloadUpdate");})
ipcMain.on("updateDesktopping", () => {autoUpdater.downloadUpdate()
})
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function handleUpdate(mainWindow, callback) {// 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包,所以设置为falseautoUpdater.autoDownload = false// 如果安装包下载好了,当应用退出后是否自动安装更新autoUpdater.autoInstallOnAppQuit = falseif (process.env.NODE_ENV == "development") {autoUpdater.updateConfigPath = path.join(__dirname, "../../aaa/app-update.yml");}// 设置版本更新服务器地址autoUpdater.setFeedURL(updateURL)// 更新发生错误时触发autoUpdater.on('error', function () {console.log(" 更新发生错误时触发",);sendUpdateMessage(message.error, "error")})// 开始检查更新事件autoUpdater.on('checking-for-update', function () {console.log(" 开始检查更新事件",);sendUpdateMessage(message.checking, "checking")})// 没有可更新版本autoUpdater.on('update-not-available', function (info) {console.log(" 开始检查更新事件",);sendUpdateMessage(message.updateNotAva, "updateNotAva")})// 发现可更新版本autoUpdater.on('update-available', function (info) {console.log(" 发现可更新版本",);// autoUpdater.downloadUpdate()sendUpdateMessage(message.updateAva, "updateAva")})// 更新下载进度事件autoUpdater.on('download-progress', function (progressObj) {console.log(" 更新下载进度事件",);sendUpdateMessage(message.updateDown, "updateDown")mainWindow.webContents.send('on-soft-download', progressObj.percent)})// 下载监听autoUpdater.on('update-downloaded',function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {mainWindow.webContents.send('on-soft-download', 100)sendUpdateMessage(message.updateSet, "updateSet")//3秒后更新setTimeout(() => {autoUpdater.quitAndInstall()}, 3000)})// 向渲染进程发送消息function sendUpdateMessage(text, type) {mainWindow.webContents.send('on-soft-message', text, type)}
}module.exports = {handleUpdate,
}
然后再 main.js 调用即可
mainWindow.on('ready-to-show', () => {mainWindow.show();updater.handleUpdate(mainWindow)if (!ipc) ipc = new IPC(mainWindow);mainWindow.openDevTools();// if(!callWindowIpc) callWindowIpc = new CallWindowIpc(mainInstance);});
main.js 所有代码
const { app, BrowserWindow, ipcMain, globalShortcut, Tray, Menu } = require('electron');
const Store = require('electron-store');
const CaptureView = require('./electron-captureview/main/captureview').default;
const path = require('path');
const url = require('url');
const TimMain = require('im_electron_sdk/dist/main');
const { SDK_APP_ID, GET_FILE_INFO_CALLBACK, SCREENSHOTMAC } = require('./const/const');
const IPC = require('./ipc');
const CallWindowIpc = require('./callWindowIpc');
const child_process = require('child_process')
const fs = require('fs')
const updater= require("./update")
const store = new Store();
Store.initRenderer();
const { autoUpdater } = require('electron-updater')process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';let callWindowIpc;
let ipc;
let mainInstance;
let catchedSdkAppId;
const settingConfig = store.get('setting-config');
const sdkappid = catchedSdkAppId = settingConfig?.sdkappId ?? SDK_APP_ID;let tray = null // 在外面创建tray变量,防止被自动删除,导致图标自动消失const initTimMain = (appid) => {mainInstance = new TimMain({sdkappid: Number(appid)});
}initTimMain(sdkappid);const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));ipcMain.handle('re-create-main-instance', async (event, newSdkAppid) => {console.log("************ re-create-main-instance", newSdkAppid)mainInstance.setSDKAPPID(newSdkAppid)return
})// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
// declare const MAIN_WINDOW_WEBPACK_ENTRY: string;// Handle creating/removing shortcuts on Windows when installing/uninstalling.
// app.on('window-all-closed', function () {
// if (process.platform !== 'darwin') app.quit()
// })const createWindow = () => {// Create the browser window.const mainWindow = new BrowserWindow({height: 628,width: 975,minWidth: 975,minHeight: 628,// show:false,icon: path.resolve(__dirname, "../../icon/logo.png"),frame: false,webPreferences: {webSecurity: true,nodeIntegration: true,nodeIntegrationInWorker: true,enableRemoteModule: true,contextIsolation: false,}});mainInstance.enable(mainWindow.webContents)global.WIN = mainWindow;mainWindow.on('ready-to-show', () => {mainWindow.show();updater.handleUpdate(mainWindow)if (!ipc) ipc = new IPC(mainWindow);mainWindow.openDevTools();// if(!callWindowIpc) callWindowIpc = new CallWindowIpc(mainInstance);});mainWindow.on('close', (e) => {// mainWindow.webContents.send('updateHistoryMessage');// setTimeout(() => {// app.exit();// }, 30);e.preventDefault(); // 阻止退出程序mainWindow.setSkipTaskbar(true) // 取消任务栏显示mainWindow.hide(); // 隐藏主程序窗口});console.log('======process env======', process.env?.NODE_ENV);if (process.env?.NODE_ENV?.trim() === 'development') {mainWindow.loadURL(`http://localhost:3000`);mainWindow.webContents.openDevTools();} else {mainWindow.loadURL(url.format({pathname: path.join(__dirname, '../../bundle/index.html'),protocol: 'file:',slashes: true}));}// 创建任务栏图标tray = new Tray(path.join(__dirname, "../../icon/logo.png"))// 自定义托盘图标的内容菜单const contextMenu = Menu.buildFromTemplate([{// 点击退出菜单退出程序label: '退出', click: function () {mainWindow.webContents.send('updateHistoryMessage');setTimeout(() => {app.exit();}, 30);// mainWindow.destroy()}}])tray.setToolTip('君凯智管') // 设置鼠标指针在托盘图标上悬停时显示的文本tray.setContextMenu(contextMenu) // 设置图标的内容菜单// 点击托盘图标,显示主窗口tray.on("click", () => {mainWindow.show();mainWindow.setSkipTaskbar(false) // 取消任务栏显示})// const capture = new CaptureView({// devTools: false,// Mosaic: false,// Text: false,// // onShow: () => {// // console.log('start screenshot');// // },// onClose: () => {// const png = clipboard.readImage().toBitmap();// const fileExample = new File([png], 'xxx.png', { type: 'image/jpeg' });// console.log('结束截图', fileExample);// },// onShowByShortCut: () => {// console.log('shortcut key to start screenshot')// }// });// capture.setMultiScreen(true);// capture.updateShortCutKey('shift+option+c');globalShortcut.register('Shift+CommandOrControl+C', function () {console.log("i am shortcut~~~~~~~~~");const newdate = new Date();const date = newdate.toISOString().replaceAll(":", "");// console.log(date.toISOString());if (process.platform == "darwin") {let ex = "screencapture -i ~/desktop/screenshot" + date + ".png"child_process.exec(`screencapture -i ~/desktop/screenshot` + date + `.png`, (error, stdout, stderr) => {if (!error) {var _img = fs.readFileSync(process.env.HOME + "/desktop/screenshot" + date + ".png");// console.log(_img);mainWindow.webContents.send(GET_FILE_INFO_CALLBACK, {triggerType: SCREENSHOTMAC,data: { _img: _img, date }})}});} else {let url = path.resolve(__dirname, "../Snipaste-2.8.2-Beta-x64/Snipaste.exe");let command = url + " snip -o C:\\Users\\Public\\Desktop\\screenshot" + date + ".png";// console.log(command);var id = setInterval(dealFile, 300);child_process.exec(command, async (error, stdout, stderr) => {if (!error) {console.log("done capture");}})function dealFile() {try {var _img = fs.readFileSync("C:\\Users\\Public\\Desktop\\screenshot" + date + ".png");clearInterval(id);console.log("file exists");console.log(_img);event.reply(GET_FILE_INFO_CALLBACK, {triggerType: SCREENSHOTMAC,data: { _img: _img, date }})} catch (err) {if (err.code == 'ENOENT') {// console.log("file doesn't exist yet")} else {throw err;}}}}})// mainWindow.loadURL(`http://localhost:3000`);// mainWindow.webContents.openDevTools();
};// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);Object.defineProperty(app, 'isPackaged', {get() {return true;}
});// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {console.log('all-window-closed');if (process.platform !== 'darwin') {app.exit();}
});app.on('activate', () => {// On OS X it's common to re-create a window in the app when the// dock icon is clicked and there are no other windows open.if (BrowserWindow.getAllWindows().length === 0) {createWindow();}
});// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
常见错误指南
Electron更新报错-Skip checkForUpdates because application is not packed and dev update config is not forced
原因是在本地启动的不是安装导致的,在根中配置即可
const { app, BrowserWindow, ipcMain, globalShortcut, Tray, Menu } = require('electron');Object.defineProperty(app, 'isPackaged', {get() {return true;}
});
Electron更新报错-
必须设置这个内容
"publish": [{"provider": "generic","channel": "latest","url": "https://jkcgy.obs.cn-south-1.myhuaweicloud.com/desktop/%E5%90%9B%E5%87%AF%E6%99%BA%E7%AE%A1%20Setup%200.0.2.exe"}],
Electron更新报错-UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '****\app-update.yml'
找不到app-update.yml 文件,可以去打包路径中的 dist\win-unpacked\resources 复制,然后通过修改 autoUpdater.updateConfigPath 路径
该路径是自定义的路径,自己在 dist\win-unpacked\resources 复制的
const { autoUpdater } = require('electron-updater') autoUpdater.updateConfigPath = path.join(__dirname, "../../aaa/app-update.yml");
Electron更新报错-找不到latest.yml 文件
找不到latest.yml文件 可以去dist 复制
这里跟配置的 autoUpdater.setFeedURL 路径有关系
const { autoUpdater } = require('electron-updater')
const updateURL = 'https://jkcgy.obs.cn-south-1.myhuaweicloud.com/desktop/'// 设置版本更新服务器地址
autoUpdater.setFeedURL(updateURL)