在WebWorker(NWJS)中不能要求节点模块

时间:2016-02-20 09:08:27

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

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

问题是,在网络工作者中,您无法使用require。所以我尝试用browserify打包ivona-node和fs(我用过这个名为browserify-fs的软件包)并用importScripts()替换require。现在我在节点模块中遇到var错误。

注意:我不认为native_fs_的方法可以用于将mp3写入磁盘(流),因为它应该是Ivona包中的错误(实际上)首先,我不知道如何解决。我包含了重现这一点的所有信息。

这是我在控制台中遇到的错误:未捕获的SyntaxError:意外的令牌变量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:

browserify main.js> ivonabundle.js

browserify 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");
}

1 个答案:

答案 0 :(得分:3)

我首先要指出的有两件事:

  1. 在网络工作者中包含节点模块
  2. 为了包含模块ivona-node,我不得不改变它的代码。当我尝试浏览它时,我收到一个错误:Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy'。检查生成的bundle.js我注意到它不包含proxy proxy.js文件夹src文件ivona-node中的模块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或使用其功能时它们可用。 http.ClientRequest就是其中之一,但正如您在此处引用的那样:strem-http,它尽可能地匹配节点的api和行为,但是某些功能不可用,因为浏览器不提供对请求的控制几乎一样多。非常重要的是类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

    负责上述错误。现在我们必须使用不同的方法来保存nodejs的数据,这让我想到了第二个问题。

    1. 从网络工作者创建文件
    2. 众所周知,从Web应用程序访问FileSystem存在许多安全问题,因此问题是我们如何从Web worker访问FileSystem。第一种方法是使用HTML5 FileSystem API。这种方法在沙箱中操作很不方便,因此如果我们在桌面应用程序中使用,则我们希望能够访问OS FileSystem。为了实现这一目标,我们可以将数据从Web worker传递到主线程,在那里我们可以使用所有Transferable Objects FileSystem功能。 Web worker提供了一个名为ivona-node的功能,您可以获得更多信息herehere,我们可以使用这些信息将从Web worker中的模块require('fs')收到的数据传递给主线程,然后以node-webkit为我们提供的方式使用browserify。这些是您可以遵循的步骤:

      1. 安装npm install -g browserify

        ivona-node
      2. 安装npm install ivona-node --save

        node_modules/ivona-node/src/main.js
      3. 转到HttpsPA = require(__dirname + '/proxy');并更改此行:

        HttpsPA = require('./proxy');

        由此:

        bundle.js

      4. 创建您的bundle.js

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

        index.html

      5. 全部放在一起

        修改Web worker和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]); }); } 的代码,以便在Web worker中接收数据并将其发送到主线程(在index.html中)

      6. 这是web worker(MyWorker.js)的代码

        <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>
        

        和index.html:

            select IntID, [1] as [ErrorID=001], [2] as [ErrorID=002], [3] as [ErrorID=003], [4] as [ErrorID=004], [5] as [ErrorID=005]
            from
            (select IntID, ErrorID, 1 as cnt
                from #YourTable) as t
            pivot
            (
                count(cnt)
                for ErrorID in ([1], [2], [3], [4], [5])
            ) as pvt