【问题标题】:Can't Require Node Modules In WebWorker (NWJS)在 WebWorker (NWJS) 中不能需要节点模块
【发布时间】:2016-06-01 23:06:45
【问题描述】:

我正在尝试做一些我认为很简单的事情。我正在使用 nwjs(以前称为 Node-Webkit),如果您不知道这基本上意味着我正在使用 Chromium 和 Node 开发桌面应用程序,其中 DOM 与 Node.js 在同一范围内。我想将工作卸载给网络工作者,这样当我将一些文本发送到 Ivona Cloud(使用 ivona-node)时 GUI 不会挂起,这是一个文本到语音 API。音频在生成并写入 MP3 时以块的形式返回。 ivona-node 使用 fs 将 mp3 写入驱动器。我让它在 dom 中工作,但需要 webworkers 才能不挂起 UI。所以我有两个节点模块需要在 webworker 中使用,ivona-node 和 fs。

问题是在 webworker 中你不能使用 require。所以我尝试用browserify打包ivona-node和fs(我使用了一个名为browserify-fs的包)并用importScripts()替换了require。现在我在节点模块中遇到 var 错误。

注意:我认为 native_fs_ 的方法不适用于将 mp3 以块(流)的形式写入磁盘,并且我在 Ivona 包中也遇到了错误(实际上首先)我不知道如何解决。我包含了所有信息以重现此内容。

这是我在控制台中遇到的错误:Uncaught SyntaxError: Unexpected token var VM39 ivonabundle.js:23132

  • 在 NWJS 中重现的步骤:

npm install ivona-node

npm install browserify-fs

npm install -g browserify

  • 现在我对 ivona-node 的 main.js 和 browserify-fs 的 index.js 进行了浏览:

浏览 main.js > ivonabundle.js

浏览 index.js > fsbundle.js


package.json...

{
  "name": "appname",
  "description": "appdescr",
  "title": "apptitle",
  "main": "index.html",
  "window":
  {
    "toolbar": true,
    "resizable": false,
    "width": 800,
    "height": 500
  },
  "webkit":
  {
    "plugin": true
  }
}

index.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.worker.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");
}

【问题讨论】:

  • 你必须把它保存到磁盘吗?如果您真的需要,您可以将工作人员的 bin 数据发送到顶部以保存,但我不明白为什么它需要保存。
  • 也许吧,但是我从 ivona-node 得到的错误呢?我没有让他们进入 dom,只是在 webworker 中。
  • Uncaught SyntaxError: Unexpected token var VM39 ivonabundle.js:23132
  • 为什么不使用require('child_process').fork?
  • 我不想加载一个新的 nwjs 实例来做这件事。这将是非常昂贵的资源。

标签: javascript node.js requirejs node-webkit web-worker


【解决方案1】:

我首先要指出两点:

  1. 在 Web Worker 中包含节点模块

为了包含模块ivona-node,我不得不稍微修改一下它的代码。当我尝试浏览它时,我收到一个错误:Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy'。检查生成的bundle.js,我注意到它不包含ivona-node 的src 文件夹中的proxy.js 文件中的模块proxy 的代码。我可以通过以下方式加载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 流,这是ClientRequest 内部的调用:stream.Writable.call(self)。因此,即使使用这种方法,我们也有明确的WritableStream:

Writable.prototype.pipe = function() {
  this.emit('error', new Error('Cannot pipe. Not readable.'));
}; 

上述错误的责任人。现在我们必须使用不同的方法来保存来自ivona-node 的数据,这留给我第二个问题。

  1. 从网络工作者创建文件

众所周知,从 Web 应用程序访问 FileSystem 存在许多安全问题,所以问题是我们如何才能从 Web Worker 访问 FileSystem。第一种方法是使用 HTML5 FileSystem API。这种方法的不便之处在于它在沙箱中运行,因此如果我们在桌面应用程序中,我们希望能够访问 OS 文件系统。为了实现这个目标,我们可以将数据从 web worker 传递到主线程,在那里我们可以使用所有nodejs FileSystem 功能。 Web Worker 提供了一个名为Transferable Objects 的功能,您可以获得更多信息here 和here,我们可以使用这些信息将从Web Worker 中的模块ivona-node 接收到的数据传递给主线程,然后使用@987654360 @ 与node-webkit 提供给我们的方式相同。这些是您可以遵循的步骤:

  1. 安装browserify

    npm install -g browserify
    
  2. 安装ivona-node

    npm install ivona-node --save
    
  3. 转到node_modules/ivona-node/src/main.js 并更改此行:

    HttpsPA = require(__dirname + '/proxy');

    通过这个:

    HttpsPA = require('./proxy');

  4. 创建您的bundle.js。

    在这里你有一些选择,创建一个bundle.js 以允许一个require() 或将一些代码放在一个文件中,其中包含一些你想要的逻辑(你实际上可以包含 web worker 的所有代码),然后创建bundle.js。在此示例中,我将创建 bundle.js 仅用于访问 require() 并在 Web Worker 文件中使用 importScripts()

    browserify -r ivona-node &gt; ibundle.js

  5. 把所有东西放在一起

    修改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]);
    });

}

和 index.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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多