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)
相关推荐
new出一个对象1 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥2 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森3 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy3 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189113 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿4 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡5 小时前
commitlint校验git提交信息
前端
虾球xz6 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇6 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒6 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript