Electron
在worker
里面上传文件(二)
前言
上期说过了
Electron
在worker_thread
里面多线程上传,但是最后有说在Electron
里面子线程无法访问主线程资源,那么导致子线程里面无法访问主线程的资源文件,worker
文件路径在build
之后,运行项目后,worker
里面不会执行。
electron-builder
在打包后默认所有的资源会被压缩到app.asar
目录下面去,asar
官方的解释是类似tar结构的一种文件,引用官方的一句话解释是ASAR
表示 Atom Shell Archive Format。 一个 asar 档案就是一个简单的 tar 文件 - 比如将那些有关联的文件放至一个单独的文件格式中。 Electron 能够任意读取其中的文件并且不需要解压整个文件。``ASAR格式是为了在Windows系统读取大量的小文件时 (比如像从node_modules加载应用的JavaScript依赖关系树) 提高性能。
其实说白了就是为了提高文件的读取的速度,同时也提高了代码的安全性,不会暴露代码。(借用vivo给asar的解释参考)
解决方案
1. 在打包的时候,将worker
文件打包到app.asar外
面,这样打包出来的worker
文件就会在打包目录下,而不是app.asar
里面。
* 配置`package.json`文件中`asarUnpack`选项,将worker文件中的三方依赖打包到app.asar外面
* `extraResources` 选项将我们自己不需要打包的worke文件依赖放到`extraResources`里面,配置如下:
"build": {
"win": {
"icon": "./dist/icons/icon.ico",
// 这里通过配置 asarUnpack选项,将一些不需要放到asar包的三方依赖代码,移动到app.asar.unpacked 目录下面,win和mac都是类似处理
"asarUnpack": [
"**/node_modules/graceful-fs/**/*",
"**/node_modules/ms/**/*",
"**/node_modules//streamroller/**/*",
"**/node_modules/date-format/**/*",
"**/node_modules/debug/**/*",
"**/node_modules/flatted/**/*",
"**/node_modules/rfdc/**/*",
"**/node_modules/streamroller/**/*",
"**/node_modules/log4js/**/*",
"**/node_modules/strnum/**/*",
"**/node_modules/esdk-obs-nodejs/**/*",
"**/node_modules/fast-xml-parser/**/*"
],
// 将worker文件打包到app.asar外面,这里可以配置具体的文件,也可以直接配置路径地址,会将整个路径下面的文件copy到to 目录下面去
"extraResources": [
{
"from": "electron/bridge/worker/upload/thread.js",
"to": "app.asar.unpacked/worker/thread.js"
}
]
},
"mac": {
"icon": "./dist/icons/icon.png",
"asarUnpack": [
"**/node_modules/graceful-fs/**/*",
"**/node_modules/ms/**/*",
"**/node_modules//streamroller/**/*",
"**/node_modules/date-format/**/*",
"**/node_modules/debug/**/*",
"**/node_modules/flatted/**/*",
"**/node_modules/rfdc/**/*",
"**/node_modules/streamroller/**/*",
"**/node_modules/log4js/**/*",
"**/node_modules/strnum/**/*",
"**/node_modules/esdk-obs-nodejs/**/*",
"**/node_modules/fast-xml-parser/**/*"
],
"extraResources": [
{
"from": "electron/bridge/worker/upload/thread.js",
"to": "app.asar.unpacked/worker/thread.js"
}
]
},
}
2. 在主线程中创建worker
,并加载worker
文件,代码如下:
const { app, BrowserWindow } = require('electron')
const path = require('path')
const fs = require('fs')
// 获取 resourcesPath 路径,*resourcesPath* 是打包后的资源目录,这里区分生产和开发环境的目录,开发环境是项目根目录,生产环境是app.asar.unpacked目录
const resourcesPath = process.resourcesPath;
const p1 = path.resolve(__dirname, "./worker/thread.js");
const p2 = path.resolve(resourcesPath, "app.asar.unpacked", "worker/thread.js");
const env = process.env.NODE_ENV;
const workerPath = env === "development" ? p1 : p2;
// resourcesPath 传入到worker里面也是为了去引入一些资源文件,在worker里面获取到 resourcePath是有问题的,所以这里传入此路径
const worker = new Worker(workerPath, { workerData: { resourcesPath } });
注意: 这里 resourcesPath 在 子线程里面是获取不到resource目录到,所以build后,引入的资源也是要区分开的,要根据 resourcesPath 去引入资源文件,不然会加载不到资源文件。
3. worker
线程文件
const { workerData, parentPort } = require("worker_threads");
const path = require("node:path");
const env = process.env.NODE_ENV;
const isDev = env === "development";
const ENV = process.env.NODE_ENV === "development" ? "test" : "prod";
// 这里获取到resourcesPath,workerData是new Worker的时候直接传入的,如果是postMessage传入的,则要根据 传入的去获取,
const resourcesPath = workerData.resourcesPath;
// 这里我写了一个工具函数,用来加载三方模块和资源文件,根据环境变量来区分开发环境和生产环境,开发环境直接引入,生产环境需要去app.asar.unpacked目录下面引入。
const loadModule = (filePath, isModule = false) => {
let realPath = "";
if (isDev) {
realPath = isModule ? filePath : path.resolve(__dirname, filePath);
} else {
realPath = path.resolve(resourcesPath, isModule ? "app.asar.unpacked/node_modules" : "app.asar.unpacked", filePath);
}
console.log("realPath", realPath);
return require(realPath);
};
var ObsClient = loadModule("esdk-obs-nodejs", true);
写在最后
到处,接上篇, electron
打包后,worker
无法加载资源文件的问题就解决了,希望对大家有所帮助。接上篇,还有两个功能没有实现。
* 通过线程池去动态管理线程,其实线程池现在有一些三方库可以解决,去管理线程池;
* 建立任务池,动态给每个`worker` 分发任务;这两个功能在后面可能会迭代,如果大家有更好的解决方案,也可以在评论区讨论哈~