2012-07-22 103 views
6

我正在使用PhantomJS中的server.listen(...)。我意識到它主要是實驗性的,它不應該用於生產。我將它用於一個簡單的截圖服務器,它接受爲URL生成屏幕截圖;這是一個我用來和PhantomJS一起玩的玩具項目。我注意到長期運行的請求的問題,特別是在response對象不可用的情況下。下面是我的代碼中的相關片段:PhantomJS:確保響應對象在server.listen中保持活動狀態(...)

var service = server.listen(8080, function (request, response) { 

    response.statusCode = 200; 

    if (loglevel === level.VERBOSE) { 
     log(request); 
    } else { 
     console.log("Incoming request with querystring:", request.url); 
    } 

    var params = parseQueryString(request.url); 
    if (params[screenshotOptions.ACTION] === action.SCREENSHOT) { 
     getScreenshot(params, function (screenshot) { 

      response.headers["success"] = screenshot.success; //<-- here is where I get the error that response.headers is unavailable. Execution pretty much stops at that point for that particular request. 
      response.headers["message"] = screenshot.message; 

      if (screenshot.success) { 
       response.write(screenshot.base64); 
      } else { 
       response.write("<html><body>There were errors!<br /><br />"); 
       response.write(screenshot.message.replace(/\n/g, "<br />")); 
       response.write("</body></html>"); 
      } 

      response.close(); 
     }); 
    } else { 
     response.write("<html><body><h1>Welcome to the screenshot server!</h1></body></html>") 
     response.close(); 
    } 
}); 

getScreenshot是使用WebPage.open(...)函數打開網頁異步方法;這個功能也是異步的。因此,似乎發生的情況是,當最終調用作爲參數傳入getScreenshot的回調時,看起來response對象已被刪除。我基本上結束了從PhantomJS以下錯誤:

Error: cannot access member `headers' of deleted QObject 

我相信這是因爲請求超時,因此連接被關閉。該文檔至少提到一次撥打response.write("")以確保連接保持打開狀態。我試着在server.listen(...)的開頭嘗試撥打response.write(""),我甚至嘗試了一個很漂亮的解決方案,我使用setInterval(...)每500毫秒執行一次response.write("")(我甚至將其降至50)。我完成後一定要清除間隔。不過,我似乎仍然遇到這個問題。

這是我不得不處理的東西,直到他們使webserver模塊更強大?或者有沒有辦法解決它?

回答

8

我能弄明白這一點。看起來,當加載某些頁面時,使用WebPage.open(例如http://fark.comhttp://cnn.com)會觸發多個onLoadFinished事件。這導致WebPage.open中的回叫被多次調用。所以會發生的是,當控制回到調用函數時,我已經關閉了響應,因此response對象不再有效。我通過在WebPage.open函數被調用之前使用創建標誌來解決這個問題。在回調中,我檢查標誌的狀態以查看是否已經遇到以前的onLoadFinished事件。一旦我在WebPage.open回調內部遇到任何問題,我會更新標誌以顯示我已完成處理。這種方式虛假(至少在我的代碼中)onLoadFinished事件不再服務。

+1

我也將服務器keepalive更改爲false,這似乎也有幫助 - service = server.listen(port,{keepAlive:false},function(request,response){.... – chrismarx 2012-10-03 02:00:24

+1

您真棒。打破你如何解決你的問題。剛剛遇到同樣的問題。 – 2012-12-30 01:24:12

+0

回覆Chris的評論 - 至少在PhantomJS 1.9.7中,['keepAlive'默認爲'false'](https://github.com/ariya/phantomjs/blob/1.9.7/src/webserver.cpp #L116-L118)。 – Whymarrh 2014-05-29 01:10:33

0

(請注意,以下是指PhantomJS 1.9.7,而OP很可能指的是1.6.1或以上)。

在多個onLoadFinished事件被解僱的情況下,你可以使用page.open()而不是自己收聽onLoadFinished。使用page.open()會將您的處理程序封裝在私人處理程序中,以確保您的回調只被調用一次。

the source

definePageSignalHandler(page, handlers, "_onPageOpenFinished", "loadFinished"); 
page.open = function (url, arg1, arg2, arg3, arg4) { 
    var thisPage = this; 
    if (arguments.length === 1) { 
     this.openUrl(url, 'get', this.settings); 
     return; 
    } 
    else if (arguments.length === 2 && typeof arg1 === 'function') { 
     this._onPageOpenFinished = function() { 
      thisPage._onPageOpenFinished = null; 
      arg1.apply(thisPage, arguments); 
     } 
     this.openUrl(url, 'get', this.settings); 
     return; 
    } 
// ... Truncated for brevity 

此功能是完全一樣的對方的回答,暴露的官方API的一部分。