说明
我也有这个问题,并通过提供我自己的实现的sun.net.www.protocol.http.HttpURLConnection
,我用它来处理任何AJAX请求解决它。我的课程便利地称为AjaxHttpURLConnection
,它挂接到getInputStream()
函数,但不返回其原始输入流。相反,我将PipedInputStream
的实例返回给WebEngine
。然后我读取来自原始输入流的所有数据,并将其传递给我的管道流。 这样,我获得了2个好处:
- 我知道什么时候收到最后一个字节,因此AJAX请求已被完全处理。
- 我甚至可以抓取所有传入的数据,并已经使用它(如果我想)。
例
首先,你将不得不告诉Java使用而不是默认的你的URLConnection实现。为此,您必须提供您自己的URLStreamHandlerFactory
版本。您可以在此处找到许多线索(例如this one)或通过Google在此主题上找到。为了设置您的工厂实例,请在main
方法的早期将其放在以下位置。这是我的样子。
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
public class MyApplication extends Application {
// ...
public static void main(String[] args) {
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
public URLStreamHandler createURLStreamHandler(String protocol) {
if ("http".equals(protocol)) {
return new MyUrlConnectionHandler();
}
return null; // Let the default handlers deal with whatever comes here (e.g. https, jar, ...)
}
});
launch(args);
}
}
其次,我们要拿出我们自己的Handler
告诉何时使用哪种类型的URLConnection
程序。
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;
public class MyUrlConnectionHandler extends Handler {
@Override
protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {
if (url.toString().contains("ajax=1")) {
return new AjaxHttpURLConnection(url, proxy, this);
}
// Return a default HttpURLConnection instance.
return new HttpURLConnection(url, proxy);
}
}
最后但并非最不重要的,这里是AjaxHttpURLConnection
。
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.io.IOUtils;
import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;
public class AjaxHttpURLConnection extends HttpURLConnection {
private PipedInputStream pipedIn;
private ReentrantLock lock;
protected AjaxHttpURLConnection(URL url, Proxy proxy, Handler handler) {
super(url, proxy, handler);
this.pipedIn = null;
this.lock = new ReentrantLock(true);
}
@Override
public InputStream getInputStream() throws IOException {
lock.lock();
try {
// Do we have to set up our own input stream?
if (pipedIn == null) {
PipedOutputStream pipedOut = new PipedOutputStream();
pipedIn = new PipedInputStream(pipedOut);
InputStream in = super.getInputStream();
/*
* Careful here! for some reason, the getInputStream method seems
* to be calling itself (no idea why). Therefore, if we haven't set
* pipedIn before calling super.getInputStream(), we will run into
* a loop or into EOFExceptions!
*/
// TODO: timeout?
new Thread(new Runnable() {
public void run() {
try {
// Pass the original data on to the browser.
byte[] data = IOUtils.toByteArray(in);
pipedOut.write(data);
pipedOut.flush();
pipedOut.close();
// Do something with the data? Decompress it if it was
// gzipped, for example.
// Signal that the browser has finished.
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
} finally {
lock.unlock();
}
return pipedIn;
}
}
进一步的考虑
- 如果您正在使用多个
WebEngine
对象,它可能会非常棘手,告诉其中一个居然开了URLConnection
,因此其浏览器已经完成加载。
- 您可能已经注意到,我只通过http连接进行身份验证。我还没有测试我的方法可以转移到https等多远(不是这里的专家:O)。
- 正如你所看到的,我知道何时使用我的
AjaxHttpURLConnection
的唯一方法是当相应的url包含ajax=1
。就我而言,这足够了。因为我对html和http不太了解,但是我不知道WebEngine
是否可以用任何不同的方式发出AJAX请求(例如头字段?)。如果有疑问,你可以简单地总是返回一个修改后的url连接的实例,但这当然意味着一些开销。
- 正如开头所述,如果您希望这样做,您可以立即使用从输入流中检索到的数据。您可以获取您的
WebEngine
以类似方式发送的请求数据。只需包装getOutputStream()
函数,并放置另一个中间流来抓取正在发送的任何内容,然后将其传递到原始输出流。
我面临同样的问题。你能找到解决方案吗? – wib
@wib:不幸的是,我尝试了一些黑客,但是问题在于Web引擎构建在库中的更深层次。最适合我的黑客只是暂停转换,让javafx线程“睡眠”一段时间,并希望js在那时完成... – Warkst
我想过这样做,但它严重依赖于可靠的Internet连接。看起来这是目前最好的解决方案 – wib