我正在使用PhantomJS中的server.listen(...)
。我意识到它主要是实验性的,不应该用于生产。我将它用于一个简单的屏幕截图服务器,它接受生成URL的屏幕截图;这是我用来玩PhantomJS的玩具项目。我注意到特别是长时间运行的请求存在问题,response
对象不可用。以下是我的代码中的相关摘要:
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("")
一次以确保连接保持打开状态。我尝试在response.write("")
的开头调用server.listen(...)
,我甚至尝试了一个非常糟糕的解决方案,我使用setInterval(...)
每500毫秒执行response.write("")
(我甚至降低了它至少50)。一旦完成,我也确保清除间隔。但是,我似乎仍然遇到了这个问题。
这是否是我必须要处理的事情,直到它们使网络服务器模块更加健壮?或者有办法吗?
答案 0 :(得分:8)
我能够弄清楚这一点。在使用WebPage.open
(例如http://fark.com
和http://cnn.com
)加载某些网页时,似乎会触发多个onLoadFinished
事件。这导致WebPage.open
中的回调被多次调用。那么当控制回到调用函数时,我已经关闭了响应,因此response
对象不再有效。我通过在调用WebPage.open
函数之前使用创建标志来修复此问题。在回调中,我检查标志的状态,看看我是否已经遇到过之前的onLoadFinished
事件。一旦我在WebPage.open
回调中使用了我必须做的任何事情,我就会更新标志以显示我已完成处理。这种方式是假的(至少在我的代码的上下文中)onLoadFinished
事件不再被提供服务。
答案 1 :(得分: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的一部分公开。