Electron实现应用更新的坑及路径的最佳实践

2022-03-09 09:08:00 浏览数 (1)

前言

应用内更新的逻辑是,应用内检测受否需要更新,如果需要更新,安装包下载到本地后,进行打开安装,同时关闭当前应用。

旧版本的API为

代码语言:javascript复制
const {shell} = window.require("electron");
shell.openItem(filepath);

旧版本是没有问题的。

新版本变更为

代码语言:javascript复制
const {shell} = window.require("electron");
shell.openPath(filepath);

但是这样就出现问题了,新版本打开是在子进程中,安装应用安装包时,应用进程是必须要关闭,如果应用进程关闭,安装包对应的子进程也会关闭,导致安装终止。

node-cmd

所以只能更换一种方式

代码语言:javascript复制
npm install node-cmd -s

在Electron中

代码语言:javascript复制
const cmd = window.require('node-cmd');
cmd.run('start "" "'   filepath   '"');

注意启动应用应该这样

代码语言:javascript复制
start "" "D:Projectmyapp.exe"

注意

  1. 路径要添加双引号,否则路径中有空格就无法打开应用。
  2. start命令后要添加"",否则打不开应用,第一个参数会被当做标题,第二个才是文件路径。

node-cmd简介

node-cmd模块中主要有run和get两类命令,其中run是执行cmd命令,get命令除了异步执行cmd命令外,在执行完毕后还会执行回调函数,返回命令行窗口的输出。

代码语言:javascript复制
var cmd = require('node-cmd');

cmd.run('touch example.created.file');

cmd.get(
  'ls',
  function(data){
    console.log('the current dir contains these files :nn',data)
  }
);

完整示例

完整的下载应用代码示例:

渲染进程中

代码语言:javascript复制
const path = window.require("path");
const {app} = window.require("electron").remote;
const cmd = window.require('node-cmd');

async update_app_action() {
  let versionpath = this.version_obj["versionpath"];
  let versioncode = this.version_obj["versioncode"];
  let file_url = `${filedownloadUrl}${versionpath}`;
  let temp_path = remote.getGlobal("sharedObject").temp_path;
  try {
    if (!fs.existsSync(temp_path)) {
      fs.mkdirSync(temp_path, {recursive: true});
    }
  } catch (e) {
  }
  let filename = versioncode   "_"   versionpath.substring(versionpath.lastIndexOf("/")   1);
  let temp_filename = "temp_"   filename;

  const filepath = path.join(
    temp_path,
    filename
  );

  const temp_filepath = path.join(
    temp_path,
    temp_filename
  );
  //文件存在直接打开
  if (fs.existsSync(filepath)) {
    cmd.run('start "" "'   filepath   '"');
  } else {
    // 不存在下载后打开
    if (fs.existsSync(temp_filepath)) {
      fs.unlinkSync(temp_filepath);
    }
    this.version_down = true;
    try {
      await this.download_file(file_url, temp_filepath);
      this.version_down = false;
      this.version_flag = false;
      fs.renameSync(temp_filepath, filepath)
      cmd.run('start "" "'   filepath   '"');
    } catch (e) {
      console.info("更新失败!")
    }
  }
}

download_file(file_url, path) {
  return new Promise((resolve, reject)=>{
    const url = require("url");
    const {http, https} = require("follow-redirects");
    let myhttp = http;
    if (file_url.indexOf("https:") !== -1) {
      myhttp = https;
    }
    const options = url.parse(file_url);
    let filldiv = document.getElementById("filldiv");
    try {
      const request = myhttp.request(options, (response)=>{
        const file_length = response.headers["content-length"];
        let downd_length = 0;
        let m_stream = fs.createWriteStream(path);
        response.on("data", (chunk)=>{
          downd_length  = chunk.length;
          let down_progress = Math.ceil((downd_length * 100) / file_length);
          this.version_progress = down_progress;
          filldiv.style.width = down_progress * 4   "px";
          //filldiv添加一个随机背景颜色
          filldiv.style.background = "#33C5B3";
          m_stream.write(chunk);
        });
        response.on("end", function() {
          m_stream.end();
          m_stream.on('close', ()=>{
            resolve(path);
          })
        });
      });
      request.end();
    } catch (e) {
      reject("下载失败!");
    }
  })
},

Electron路径最佳实践

如果渲染进程太多,不建议在渲染进程中获取路径,建议在主进程中设置。

主进程

代码语言:javascript复制
const {app} = require("electron");
const path = require("path");
const fs = require("fs");

global.sharedObject = {
  temp_path: "",
}

let basepath = "";

try {
  basepath = app.getPath("downloads");
} catch (e) {
  basepath = path.dirname(app.getPath("exe"));
}

if (/.*[u4e00-u9fa5 ] .*$/.test(basepath)) {
  basepath = "C:\";
}

let temp_path = path.join(
  basepath,
  "school_live_temp"
)

try {
  if (!fs.existsSync(temp_path)) {
    fs.mkdirSync(temp_path, {recursive: true});
  }
} catch (e) {
}

console.info("temp_path", temp_path);
global.sharedObject.temp_path = temp_path;

注意

在有些电脑上竟然无法使用app.getPath("downloads")获取路径,所以这里进行异常捕获。 有些电脑用户名是中文,而某些SDK不支持路径中包含中文和空格,这里也做了判断。

渲染进程

代码语言:javascript复制
const remote = window.require("electron").remote;
let temp_path = remote.getGlobal("sharedObject").temp_path;

0 人点赞