2016-02-20 73 views
7

我試圖做一些我認爲很簡單的事情。我正在使用nwjs(以前稱爲Node-Webkit),如果您不知道,這基本上意味着我正在使用Chromium開發桌面應用程序&節點,其中DOM與節點的範圍相同。我想將工作卸載到webworker,以便在我將一些文本發送到Ivona Cloud(使用ivona-node)文本到語音API時,GUI不會掛起。音頻在生成時會以塊形式返回,並寫入MP3。 ivona-node使用fs將mp3寫入驅動器。我在dom工作,但需要web工作人員不要掛上用戶界面。所以我需要在webworker,ivona-node和fs中使用兩個節點模塊。無法在WebWorker中需要節點模塊(NWJS)

問題是,在webworker中你不能使用require。所以我嘗試用browserify打包ivona-node和fs(我使用了一個名爲browserify-fs的包),並用importScripts()替換require。現在,我在節點模塊中收到了var錯誤。

注意:我不認爲native_fs_的方法可以用來將mp3寫入磁盤(流),因爲它應該是這樣,並且Ivona軟件包中也出現錯誤(實際上是首先和最重要的),我不知道如何解決。我包括所有信息來重現此。

下面是我收到的控制檯錯誤:未捕獲的SyntaxError:意外的標記VAR VM39 ivonabundle.js:23132個

  • 重現步驟在NWJS:

NPM安裝IVONA節點

NPM安裝browserify-FS

NPM安裝-g browserify

  • 現在我browserified爲browserify-FS爲IVONA節點main.js和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"); 
} 
+0

你必須將它保存到磁盤?您可以將工作人員的箱數據發送到頂部以保存您是否真的需要,但我不明白爲什麼需要保存。 – dandavis

+0

也許,但是我從ivona-node獲得的錯誤呢?我只是在網絡工作者那裏沒有得到他們。 – xendi

+0

未捕獲的SyntaxError:意外的令牌var VM39 ivonabundle.js:23132 – xendi

回答

3

有兩件事情,我婉首先指出:

  1. 包括節點模塊在網絡工作者

爲了包括模塊ivona-node我不得不改變一點我ts代碼。當我嘗試瀏覽它時,出現錯誤:Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy'。檢查生成的bundle.js我注意到它不包含proxy的代碼,該代碼位於ivona-nodesrc文件夾中的文件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環境創造產生這種說法Stream.call(this)允許請求使用該方法pipeOutgoingMessage,但是當你調用https.requestbrowserify版本的結果是一個Writable流,這是ClientRequest中的呼叫:stream.Writable.call(self)。因此,我們必須明確一個WritableStream即使有這樣的方法:

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

該負責上述錯誤的。現在我們必須使用不同的方法來保存來自ivona-node的數據,這使我可以回到第二個問題。

  • 從一個網絡工作者
  • 是衆所周知,其從Web應用程序訪問文件系統有許多安全問題,創建一個文件,所以這個問題是我們如何可以從Web Worker訪問FileSystem。第一種方法是使用HTML5 FileSystem API。這種方法不方便,它在沙箱中運行,所以如果我們在桌面應用程序中,我們想要訪問操作系統文件系統。爲了實現這個目標,我們可以將數據從web worker傳遞到主線程,在那裏我們可以使用所有的文件系統功能。網絡工作者提供了一個叫Transferable Objects功能,你可以得到更多的信息herehere,我們可以用它來在網絡工作者從模塊ivona-node接收到的數據傳遞給主線程,然後以同樣的方式,node-webkit提供給我們使用require('fs')。這些步驟可以遵循:

    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()或把一些代碼放在一個文件,你想要什麼一些邏輯(你實際上可以包括網絡工作者的所有代碼),然後創建bundle.js。在這個例子中,我將創建bundle.js只爲有機會獲得require(),爲了使用importScripts()在網絡工作者文件

      browserify -r ivona-node > ibundle.js

    5. 把所有在一起

      修改網絡工作者的代碼和index.html在網絡工作者接收數據並將其發送到主線程(在index.html中)

    這是web窩的代碼rker(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>