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

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

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

注意:我不认为native_fs_的方法可以用来将mp3写入磁盘(流),因为它应该是这样,并且Ivona包中也出现错误(实际上是第一个也是最重要的),我不会不知道如何解决。 我包括所有信息来重现此。

这是我在控制台中得到的一个错误:Uncaught SyntaxError:意外的令牌var VM39 ivonabundle.js:23132

  • 在NWJS中重现的步骤:
  • npm安装ivona-node

    npm安装browserify-fs

    npm install -g browserify

  • 现在,我浏览了针对browserify-fs的ivona-node和index.js的main.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");
    }
    

    我首先要指出两点:

  • 在网络工作者中包含节点模块
  • 为了包含模块ivona-node我不得不改变它的一些代码。 当我尝试浏览它时,出现错误: Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy' 。 检查bundle.js生成的I注意到它不包括模块的代码proxy这是在文件proxy.jssrc的文件夹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. 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应用程序访问FileSystem有许多安全问题,所以问题是我们如何从Web Worker访问FileSystem。 第一种方法是使用HTML5 FileSystem API。 这种方法不方便,它在沙箱中运行,所以如果我们在桌面应用程序中,我们想要访问操作系统文件系统。 为了实现这个目标,我们可以将数据从web worker传递到主线程,在那里我们可以使用所有nodejs FileSystem功能。 Web工作者提供了一个名为Transferable Objects的功能,您可以在这里和这里获得更多信息,我们可以使用ivona-node web worker中模块ivona-node收到的数据传递给主线程,然后使用require('fs')node-webkit为我们提供的方式相同。 这些是您可以遵循的步骤:

  • 安装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仅用于访问require()并在web worker文件中使用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]);
        });
    
    }
    

    和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>
    
    链接地址: http://www.djcxy.com/p/32169.html

    上一篇: Can't Require Node Modules In WebWorker (NWJS)

    下一篇: How to read reminders in google calendars