electron + go 如何从sqlite获取数据

时间:2023-01-04 15:07:21

我现在的数据在sqlite中,保存在mac本地的一个文件中。用了electron+vue搭建了一个客户端。

electron + go 如何从sqlite获取数据

我大概希望是这样的逻辑,先加载本地db文件,然后再获取数据。

这里就有一个问题,我怎么获取sqlite中的数据呢?从哪里加载呢?

思考

electron的进程分为master Process 和 renderer Process。这个事情是哪个Process来做呢?

electron + go 如何从sqlite获取数据

我觉得这里可能有好几种方案。

首先第一种,直接从RendererProcess来,这就和浏览器要直接访问本地文件的逻辑一样了。肯定可以做,不过感觉会很不安全。

第二种,就是MainProcess来读取本地文件,然后传递到Renderer来显示。这个其实是更符合electron的设计思路。因为mainProcess就是用来处理和本机的连接的。

其实还有第三种,就是启动一个本地服务器,来读取sqlite,提供接口出来给RendererProcess。然后RendererProcess就和正常的js调用接口一样,调用这个本地服务器。这个的好处就是这个本地服务器可以用其他语言来做,这里我非常希望用golang来实现。其次,这个本地服务器其实很容易修改为远端服务器。

所以我想尝试一下第三种方式来实现。

尝试eletron+go

这个方案和hade的设计思路是一致的,hade可以构建前端eletron,再构建一个后端go的二进制文件,然后eletron打包的时候把后端go的二进制文件打包进去。

那么这里就遇到第一个问题,如何在eletron中打包一个二进制文件,并且启动它呢?

如何在eletron中打包二进制文件?

找了一些资料,特别是这篇文章:

https://*.com/questions/33152533/bundling-precompiled-binary-into-electron-app/45152204#45152204

尝试了一下,真的是可行的。

首先我用golang编译出二进制文件叫toolbox-server

然后存放的目录是resources/mac/toolbox-server:

electron + go 如何从sqlite获取数据

接着修改vue.config.js文件(这个修改的文件如果你没有使用vue来编译electron的话,你就修改package.json)

package.json增加字段:

"extraFiles": [
    {
      "from": "resources/${os}",
      "to": "Resources/bin",
      "filter": [
        "**/*"
      ]
    }
  ]

vue.config.js修改字段:

...

module.exports = defineConfig({
  ...
  pluginOptions: {
    electronBuilder: {
      builderOptions: {
        extraFiles: [
          {
            "from": "resources/${os}",
            "to": "Resources/bin",
            "filter": [
              "**/*"
            ]
          }
        ],
      }
    }
  }
})

不管是在vue.config.js还是在package.json中修改,都以为着告诉electron打包程序,我需要把resources/${os}/下的所有文件放到打包后的Resources/bin文件下。

然后在我们的electron的main.js(在我项目中是background.js)。都是代表electron的主进程。

在app的ready事件中,我注入了以下代码:

import appRootDir from 'app-root-dir';
const log = require('electron-log');
...
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS3_DEVTOOLS)
    } catch (e) {
      log.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createWindow()

  const execPath = (!isDevelopment) ?
    joinPath(appRootDir.get(), 'bin') :
    joinPath(appRootDir.get(), 'resources', getPlatform());

  const cmd = `${joinPath(execPath, 'toolbox-server')}` + ' app start';
  log.info(cmd);

  exec(cmd, (err, stdout, stderr) => {
    if (err != null) {
      log.error('run error' + err.toString());
    }
    log.log('stdout:' + stdout.toString());
    log.error('stderr:' + stderr.toString());
  });
})

其中的log包我使用的是 electron-log。这样才能最后在编译的时候,把日志打印到

~/Library/Logs/{app_name}/ 

对于execPath我们可以再看下,这里使用了 app-root-dir 包,它对应的 appRootDir.get() 方法在运行的时候,对应的目录为:

/Applications/{app_name}.app/Contents/Resources/

其实这里我琢磨可能不用app-root-dir包也行,直接用electron的app带的各种路径方法(https://www.electronjs.org/zh/docs/latest/api/app),不过这里我没有继续尝试了。

然后使用npm run electron:build 就可以看到生成打包文件。

安装打包文件,通过应用程序-{app_name}-Content可以看到二进制的Golang文件就在这里面了。

electron + go 如何从sqlite获取数据

同时如果你在调试模式下使用 npm run electron:serve, 可以看到在electron启动的同时,这个进程也就起来了。

![image-20230101165421154](../../../Library/Application Support/typora-user-images/image-20230101165421154.png)

且端口在localhost中可用

electron + go 如何从sqlite获取数据

打包二进制文件进electron完成。

ps: 研究这个过程中,这篇(https://ld246.com/article/1547556984481)对我的帮助很大,mark下。

golang实现的http接口

剩下的就很简单了,golang写一个http接口,读取sqlite。

我使用hade框架,

electron + go 如何从sqlite获取数据

很快就可以完成类似的接口

http://127.0.0.1:8070/essay/list

electron + go 如何从sqlite获取数据

这里就没有什么好说的了。

electron的renderer调用server获取数据展示

这个也没有什么好说的了,就是基本的vue来调用http获取数据。

fetchData() {
      request({
        url: "/essay/list?page=" + this.next_page + "&size=" + this.size,
        method: "get",
      }).then((res) => {
        console.log(res);
        if (res.status === 200) {
          if (res.data.list.length === 0) {
            this.noMore = true;
            return;
          }
          for (let i= 0; i < res.data.list.length; i++) {
            this.data.push(res.data.list[i])
          }
          this.page = this.next_page;
          this.next_page = this.next_page + 1;
          this.loading = false;
        }
      });
    },

二进制打包还有一些问题

二进制打包这里还有一些问题,在打包的时候,需要配置文件,但是我目前是没有的。这个怎么办呢?

还有就是toolbox-server会生成运行时文件,都在storage里面,这个怎么办呢?

打包配置文件

首先我把配置文件也都复制到resources中。

![image-20230102125854304](../../../Library/Application Support/typora-user-images/image-20230102125854304.png)

这样vue.config.js中复制的时候也会把整个目录进行拷贝。

electron + go 如何从sqlite获取数据

接着由于我的程序运行的时候需要在toolbox-server同级目录运行,所以我修改了一下启动toolbox-server 的程序;

async function startToolboxServer() {
  const execPath = (!isDevelopment) ?
      joinPath(appRootDir.get(), 'bin', 'toolbox-server') :
      joinPath(appRootDir.get());

  const cmd = `cd ${execPath} && ${joinPath(execPath, 'toolbox-server')}` + ' app start';
  log.info(cmd);

  exec(cmd, (err, stdout, stderr) => {
    if (err != null) {
      log.error('run error' + err.toString());
    }
    log.log('stdout:' + stdout.toString());
    log.error('stderr:' + stderr.toString());
  });
}

这里主要加上了cd ${execPath} 目录。

运行日志放在应用对应的日志目录中

eletron对应的日志目录为:~/Library/Logs/{app_name}/

我们希望在这个目录下能创建一个子目录toolbox-server,把toolbox-server的运行日志放在里面。即(~/Library/Logs/toolbox/toolbox-server/)

这里就首先需要 toolbox-server 这个程序是支持修改运行日志的。

所幸hade框架是支持环境变量设置运行日志:

http://hade.funaio.cn/guide/app.html#进程运行基础配置

STORAGE_FOLDER=/Users/jianfengye/Documents/workspace/gohade/hade/teststorage ./hade app start

所以我可以通过在electron的主进程启动toolbox-server的地方设置环境变量来达到这个目的。

我修改了一下startToolboxServer 启动toolbox-server的函数

async function startToolboxServer() {
  const execPath = (!isDevelopment) ?
      joinPath(appRootDir.get(), 'bin', 'toolbox-server') :
      joinPath(appRootDir.get());

  const cmd = `cd ${execPath} && ${joinPath(execPath, 'toolbox-server')}` + ' app start';

  let logFolder = joinPath(app.getPath("logs"), "toolbox-server");
  let envVars = {...process.env}
  if (!isDevelopment) {
    envVars = { ...process.env, STORAGE_FOLDER:  logFolder}
  }

  log.info("cmd: " + cmd + ", env: " + JSON.stringify({STORAGE_FOLDER : logFolder}));

  exec(cmd, {env: envVars}, (err, stdout, stderr) => {
    if (err != null) {
      log.error('run error' + err.toString());
    }
    log.log('stdout:' + stdout.toString());
    log.error('stderr:' + stderr.toString());
  });
}

主要是在正式环境中把STORAGE_FOLDER的环境变量设置为 joinPath(app.getPath("logs"), "toolbox-server")

这里的exec是child_prcess包的函数,增加env的方法可以参考:https://nodejs.org/api/child_process.html

这里的app.getPath("logs") 是electron自带的方法,具体可以参考:https://www.electronjs.org/zh/docs/latest/api/app

于是这样设置之后,hade框架生成的strorage运行产生文件就放在了 ~/Library/Logs/toolbox 中了,完美。