electron + go 如何從sqlite獲取資料

2023-01-04 15:00:38

我現在的資料在sqlite中,儲存在mac原生的一個檔案中。用了electron+vue搭建了一個使用者端。

我大概希望是這樣的邏輯,先載入本地db檔案,然後再獲取資料。

這裡就有一個問題,我怎麼獲取sqlite中的資料呢?從哪裡載入呢?

思考

electron的程序分為master Process 和 renderer Process。這個事情是哪個Process來做呢?

我覺得這裡可能有好幾種方案。

首先第一種,直接從RendererProcess來,這就和瀏覽器要直接存取本地檔案的邏輯一樣了。肯定可以做,不過感覺會很不安全。

第二種,就是MainProcess來讀取本地檔案,然後傳遞到Renderer來顯示。這個其實是更符合electron的設計思路。因為mainProcess就是用來處理和本機的連線的。

其實還有第三種,就是啟動一個本地伺服器,來讀取sqlite,提供介面出來給RendererProcess。然後RendererProcess就和正常的js呼叫介面一樣,呼叫這個本地伺服器。這個的好處就是這個本地伺服器可以用其他語言來做,這裡我非常希望用golang來實現。其次,這個本地伺服器其實很容易修改為遠端伺服器。

所以我想嘗試一下第三種方式來實現。

嘗試eletron+go

這個方案和hade的設計思路是一致的,hade可以構建前端eletron,再構建一個後端go的二進位制檔案,然後eletron打包的時候把後端go的二進位制檔案打包進去。

那麼這裡就遇到第一個問題,如何在eletron中打包一個二進位制檔案,並且啟動它呢?

如何在eletron中打包二進位制檔案?

找了一些資料,特別是這篇文章:

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

嘗試了一下,真的是可行的。

首先我用golang編譯出二進位制檔案叫toolbox-server

然後存放的目錄是resources/mac/toolbox-server:

接著修改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檔案就在這裡面了。

同時如果你在偵錯模式下使用 npm run electron:serve, 可以看到在electron啟動的同時,這個程序也就起來了。

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

且埠在localhost中可用

打包二進位制檔案進electron完成。

ps: 研究這個過程中,這篇(https://ld246.com/article/1547556984481)對我的幫助很大,mark下。

golang實現的http介面

剩下的就很簡單了,golang寫一個http介面,讀取sqlite。

我使用hade框架,

很快就可以完成類似的介面

http://127.0.0.1:8070/essay/list

這裡就沒有什麼好說的了。

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中複製的時候也會把整個目錄進行拷貝。

接著由於我的程式執行的時候需要在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 中了,完美。