6

我正在使用server.listen(...)PhantomJS。我意识到它在很大程度上是实验性的,不应该在生产中使用。我将它用于一个简单的屏幕截图服务器,它接受为 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(...)我什至尝试了一个非常 hacky 的解决方案,我曾经每 500 毫秒setInterval(...)执行response.write("")一次(我什至将它降低到低至 50)。完成后,我还确保清除间隔。但是,我似乎仍然遇到这个问题。

在他们使 webserver 模块更健壮之前,这是我必须处理的事情吗?或者有没有办法解决它?

4

2 回答 2

8

我能够弄清楚这一点。似乎在加载某些页面时WebPage.open(例如http://fark.comhttp://cnn.com)会触发多个onLoadFinished事件。这会导致回调WebPage.open被多次调用。所以发生的情况是,当控制权返回到调用函数时,我已经关闭了响应,因此response对象不再有效。WebPage.open我通过在调用函数之前创建一个标志来解决这个问题。在回调中,我检查标志的状态以查看我是否已经遇到过以前的onLoadFinished事件。一旦我在WebPage.open回调中完成了我必须做的任何事情,我就会更新标志以表明我已经完成了处理。onLoadFinished这种方式不再为虚假(至少在我的代码上下文中)事件提供服务。

于 2012-07-22T18:41:27.477 回答
0

(请注意,以下指的是 PhantomJS 1.9.7,而 OP 可能指的是 1.6.1 或更早版本。)

如果触发了多个onLoadFinished事件,您可以使用page.open()而不是自己监听onLoadFinished。Usingpage.open()会将您的处理程序包装在一个私有处理程序中,以确保您的回调只被调用一次。

来源

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 的一部分公开。

于 2014-05-29T01:30:46.867 回答