不能在 WebWorker (NWJS) 中要求节点模块
Can't Require Node Modules In WebWorker (NWJS)
我正在尝试做一些我认为很简单的事情。我正在使用nwjs(以前称为Node-Webkit),如果您不知道,这基本上意味着我正在使用Chromium和Node开发桌面应用程序,其中DOM与Node处于同一范围内。我想将工作卸载给网络工作者,以便在我将一些文本发送到 Ivona Cloud(使用 ivona-node)时 GUI 不会挂起,这是一个文本到语音 API。音频在生成时以块的形式返回并写入 MP3。Ivona-node使用FS将MP3写入驱动器。我让它在 dom 中工作,但需要网络工作者不要挂起 UI。所以我有两个节点模块需要在 webworker 中使用,ivona-node 和 fs。
问题是在 Web 工作者中你不能使用 require。所以我尝试用browserify打包ivona-node和fs(我使用了一个名为browserify-fs的软件包)并将require替换为importScripts()。现在我在节点模块中收到 var 错误。
注意:我认为native_fs_的方法不适用于将 mp3 以块(流)的形式写入磁盘,而且我在 Ivona 包中也遇到了错误(实际上首先是)我不知道如何修复。我正在包括所有信息来重现这一点。
这是我在控制台中遇到的一个错误:未捕获的语法错误:意外的令牌变量 VM39 ivonabundle.js:23132
- 在 NWJS 中重现的步骤:
npm install ivona-node
npm install browserify-fs
npm install -g browserify
- 现在我浏览器化了main.js用于ivona-node和index.js用于browserify-fs:
Browserify main.js> ivonabundle.js
browserify index.js> fsbundle.js
包.json...
{
"name": "appname",
"description": "appdescr",
"title": "apptitle",
"main": "index.html",
"window":
{
"toolbar": true,
"resizable": false,
"width": 800,
"height": 500
},
"webkit":
{
"plugin": true
}
}
索引.html...
<html>
<head>
<title>apptitle</title>
</head>
<body>
<p><output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<br><br>
<script>
var w;
function startWorker() {
if(typeof(Worker) !== "undefined") {
if(typeof(w) == "undefined") {
w = new Worker("TTMP3.worker.js");
w.postMessage(['This is some text to speak.']);
}
w.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data;
};
} else {
document.getElementById("result").innerHTML = "Sorry! No Web Worker support.";
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
</script>
</body>
</html>
TTMP3.工人.js...
importScripts('node_modules/browserify-fs/fsbundle.js','node_modules/ivona-node/src/ivonabundle.js');
onmessage = function T2MP3(Text2Speak)
{
postMessage(Text2Speak.data[0]);
//var fs = require('fs'),
// Ivona = require('ivona-node');
var ivona = new Ivona({
accessKey: 'xxxxxxxxxxx',
secretKey: 'xxxxxxxxxxx'
});
//ivona.listVoices()
//.on('end', function(voices) {
//console.log(voices);
//});
// ivona.createVoice(text, config)
// [string] text - the text to be spoken
// [object] config (optional) - override Ivona request via 'body' value
ivona.createVoice(Text2Speak.data[0], {
body: {
voice: {
name: 'Salli',
language: 'en-US',
gender: 'Female'
}
}
}).pipe(fs.createWriteStream('text.mp3'));
postMessage("Done");
}
我想先指出两件事:
- 在 Web 工作线程中包含节点模块
为了包含模块ivona-node
我不得不稍微更改其代码。当我尝试将其浏览器化时,出现错误:Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy'
。检查生成的bundle.js
,我注意到它不包括模块proxy
的代码,该代码位于ivona-node
src
文件夹中的文件proxy.js
中。我可以通过以下方式加载proxy
模块更改此行HttpsPA = require(__dirname + '/proxy');
:HttpsPA = require('./proxy');
.之后,ivona-node
可以通过browserify
加载到客户端。然后,在尝试遵循该示例时,我遇到了另一个错误。原来这段代码:
ivona.createVoice(Text2Speak.data[0], {
body: {
voice: {
name: 'Salli',
language: 'en-US',
gender: 'Female'
}
}
}).pipe(fs.createWriteStream('text.mp3'));
不再正确,它会导致错误: Uncaught Error: Cannot pipe. Not readable.
这里的问题出在模块http
. 该模块browserify
包装了许多内置的npm
模块,这意味着当您使用require()
或使用其功能时,它们可用。 http
就是其中之一,但正如您可以在此处参考的那样:strem-http,它试图尽可能匹配节点的 API 和行为,但某些功能不可用,因为浏览器无法对请求进行尽可能多的控制。非常重要的是类http.ClientRequest
的事实,这个类nodejs
环境中创建一个生成此语句的OutgoingMessage
,Stream.call(this)
允许在请求中使用方法pipe
,但是在browserify
版本中,当您调用https.request
时,结果是Writable
Stream,这是ClientRequest
内的调用: stream.Writable.call(self)
.因此,即使使用此方法,我们也明确地WritableStream
:
Writable.prototype.pipe = function() {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
上述错误的责任人。现在我们必须使用不同的方法来保存ivona-node
中的数据,这让我进入第二个问题。
- 从 Web 辅助角色创建文件
众所周知,从Web应用程序访问文件系统存在许多安全问题,因此问题在于我们如何从Web工作者访问文件系统。第一种方法是使用 HTML5 文件系统 API。这种方法在沙箱中运行的不便之处,因此,如果我们在桌面应用程序中,我们希望访问操作系统文件系统。为了实现这一目标,我们可以将数据从 Web worker 传递到主线程,在那里我们可以使用所有nodejs
文件系统功能。 Web worker 提供一个名为 Transferable Objects
,您可以在此处和此处获取更多信息,我们可以使用这些信息将从 Web worker 中的模块ivona-node
接收的数据传递到主线程,然后以与node-webkit
相同的方式使用require('fs')
提供给我们。 您可以按照以下步骤操作:
安装
browserify
npm install -g browserify
安装
ivona-node
npm install ivona-node --save
转到
node_modules/ivona-node/src/main.js
并更改此行:HttpsPA = require(__dirname + '/proxy');
通过这个:
HttpsPA = require('./proxy');
创建您的
bundle.js
.在这里,您有一些替代方案,创建一个
bundle.js
以允许require()
或将一些代码放入具有所需逻辑的文件中(您实际上可以包含 Web worker 的所有代码),然后创建bundle.js
。在此示例中,我将仅创建bundle.js
以访问 Web worker 文件中的require()
和使用importScripts()
browserify -r ivona-node > ibundle.js
把所有放在一起
修改 Web worker和
index.html
的代码,以便在 Web worker 中接收数据并将其发送到主线程(在 index.html 中)
这是 Web worker 的代码 (MyWorker.js)
importScripts('ibundle.js');
var Ivona = require('ivona-node');
onmessage = function T2MP3(Text2Speak)
{
var ivona = new Ivona({
accessKey: 'xxxxxxxxxxxx',
secretKey: 'xxxxxxxxxxxx'
});
var req = ivona.createVoice(Text2Speak.data[0], {
body: {
voice: {
name: 'Salli',
language: 'en-US',
gender: 'Female'
}
}
});
req.on('data', function(chunk){
var arr = new Uint8Array(chunk);
postMessage({event: 'data', data: arr}, [arr.buffer]);
});
req.on('end', function(){
postMessage(Text2Speak.data[0]);
});
}
和索引.html:
<html>
<head>
<title>apptitle</title>
</head>
<body>
<p><output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<br><br>
<script>
var w;
var fs = require('fs');
function startWorker() {
var writer = fs.createWriteStream('text.mp3');
if(typeof(Worker) !== "undefined") {
if(typeof(w) == "undefined") {
w = new Worker("MyWorker.js");
w.postMessage(['This is some text to speak.']);
}
w.onmessage = function(event) {
var data = event.data;
if(data.event !== undefined && data.event == 'data'){
var buffer = new Buffer(data.data);
writer.write(buffer);
}
else{
writer.end();
document.getElementById("result").innerHTML = data;
}
};
} else {
document.getElementById("result").innerHTML = "Sorry! No Web Worker support.";
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
</script>
</body>
</html>
- 节点模块依赖关系
- 如何引用HTML中节点模块中的js文件
- 未定义应为节点模块的对象
- 如何在客户端javascript中需要节点模块
- 如何在节点模块中从命令行运行函数
- 将节点模块添加到gitignore
- 使用Q节点模块时,Bluemix node.js应用程序部署失败
- 使用document.cookie将客户端javascript转换为节点模块
- 查找与锁定和更新调度相关的一个或两个节点模块
- 节点模块的可能结构
- 节点模块对象范围:在所有函数之间共享一个对象
- 有没有一种方法可以使用javascript和节点模块(oracledb)将csv导入oracle数据库
- 单元测试节点模块与mocha,模块变量行为怪异
- 要安装依赖项的自定义节点模块
- Node.js如何将链接标记解析为节点模块
- 如何公开JS节点模块
- 如何访问节点模块中具有相对路径的文件
- 节点模块和常规javascript文件之间的区别是什么
- Npm.require可以'找不到节点模块
- 将参数传递给基于类的节点模块