electron 如何升级版本

electron-updater使用指南

基础

检测是否最新版

autoUpdater.checkForUpdates()

下载最新版

autoUpdater.downloadUpdate()

项目使用

update.js

javascript 复制代码
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,当点击检测到新版本时,会自动下载安装包,所以设置为false
  autoUpdater.autoDownload = false

  // 如果安装包下载好了,当应用退出后是否自动安装更新
  autoUpdater.autoInstallOnAppQuit = false
  if (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 调用即可

javascript 复制代码
  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 所有代码

javascript 复制代码
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

原因是在本地启动的不是安装导致的,在根中配置即可

javascript 复制代码
const { app, BrowserWindow, ipcMain, globalShortcut, Tray, Menu } = require('electron');

Object.defineProperty(app, 'isPackaged', {
  get() {
    return true;
  }
});

Electron更新报错-

必须设置这个内容

javascript 复制代码
  "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 复制的

javascript 复制代码
const { autoUpdater } = require('electron-updater') 
 autoUpdater.updateConfigPath = path.join(__dirname, "../../aaa/app-update.yml");

Electron更新报错-找不到latest.yml 文件

找不到latest.yml文件 可以去dist复制

这里跟配置的 autoUpdater.setFeedURL 路径有关系

javascript 复制代码
const { autoUpdater } = require('electron-updater')
const updateURL = 'https://jkcgy.obs.cn-south-1.myhuaweicloud.com/desktop/'
 // 设置版本更新服务器地址
autoUpdater.setFeedURL(updateURL)
相关推荐
该用户已不存在8 小时前
这6个网站一旦知道就离不开了
前端·后端·github
Ai行者心易8 小时前
10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
前端·后端
东东2338 小时前
前端开发中如何取消Promise操作
前端·javascript·promise
掘金安东尼8 小时前
官方:什么是 Vite+?
前端·javascript·vue.js
柒崽8 小时前
ios移动端浏览器,vh高度和页面实际高度不匹配的解决方案
前端
渣哥9 小时前
你以为 Bean 只是 new 出来?Spring BeanFactory 背后的秘密让人惊讶
javascript·后端·面试
烛阴9 小时前
为什么游戏开发者都爱 Lua?零基础快速上手指南
前端·lua
大猫会长9 小时前
tailwindcss出现could not determine executable to run
前端·tailwindcss
Moonbit9 小时前
MoonBit Pearls Vol.10:prettyprinter:使用函数组合解决结构化数据打印问题
前端·后端·程序员
533_9 小时前
[css] border 渐变
前端·css