Skip to main content
 首页 » 编程设计

browser中使用 JavaFX WebView 下载

2025年02月15日11think

我的网络应用程序提供下载。 JavaScript 在点击时创建 url(这取决于用户输入),浏览器应该打开它,这样页面就不会重新加载。

为此,我认为我必须有其他选择:

// Alt1: 
window.open(pathToFile); 
 
// Alt2: 
var downloadFrame = document.getElementById('downloads'); 
if (downloadFrame === null) { 
  downloadFrame = document.createElement('iframe'); 
  downloadFrame.id = 'downloads'; 
  downloadFrame.style.display = 'none'; 
  document.body.appendChild(downloadFrame); 
} 
downloadFrame.src = pathToFile; 

两者都可以在 Firefox 下运行。打开新窗口方法的问题:如果在服务器上创建文件需要更多时间,则新的空选项卡将稍后关闭。 iframe 的问题:如果服务器出现错误,则不会给出任何反馈。

我认为在 Firefox 中 iframe 是更好的解决方案。但 Web 应用程序也必须使用 JavaFX WebView 运行。 JavaFX没有下载功能,我必须自己编写。一种可能的方法是监听位置属性:

final WebView webView = new WebView(); 
 
webView.getEngine().locationProperty().addListener(new ChangeListener<String>() { 
    @Override public void changed(ObservableValue<? extends String> observableValue, String oldLoc, String newLoc) { 
        if (newLoc.cotains("/download")) { 
            FileChooser chooser = new FileChooser(); 
            chooser.setTitle("Save " + newLoc); 
            File saveFile = chooser.showSaveDialog(webView.getEngine().getScene().getWindow()); 
 
            if (saveFile != null) { 
                BufferedInputStream  is = null; 
                BufferedOutputStream os = null; 
                try { 
                    is = new BufferedInputStream(new URL(newLoc).openStream()); 
                    os = new BufferedOutputStream(new FileOutputStream(saveFile)); 
 
                    while ((readBytes = is.read()) != -1) { 
                      os.write(b); 
                    } 
                } finally { 
                    try { if (is != null) is.close(); } catch (IOException e) {} 
                    try { if (os != null) os.close(); } catch (IOException e) {} 
                } 
            } 
        } 
    } 
} 

存在一些问题:

  1. 下载开始取决于url的一部分,因为JafaFX不支持访问http header (这是可以忍受的)
  2. 如果用户使用相同的 URL 开始下载两次,则只有第一次下载有效(仅当 URL 是新的时才会触发更改事件)。我可以创建唯一的 url(末尾带有 #1、#2 等)。但这很难看。
  3. 只有“window.open(pathToFile);”方法有效。加载 iframe 不会触发网站的更改位置事件。这是可以预料到的,但我还没有找到合适的监听器。

有人可以帮我解决2.或3.吗?

谢谢!

PS:抱歉我的英语不好。

编辑: 对于2.我找到了一种方法。我不知道它是否好,是否高性能,新的 webview 是否被删除或下载后在缓存中,...... 当出现某些问题时,用户不会得到反馈:

webView.getEngine().setCreatePopupHandler(new Callback<PopupFeatures, WebEngine>() { 
    @Override public WebEngine call(PopupFeatures config) { 
        final WebView downloader = new WebView(); 
        downloader.getEngine().locationProperty().addListener(/* The Listener from above */); 
        return downloader.getEngine(); 
    } 
} 

请您参考如下方法:

我认为您可能只需要使用 copyURLtoFile 来获取文件...在位置更改时调用该文件,或者仅使用注册的 java 类调用该文件。像这样的事情:

org.apache.commons.io.FileUtils.copyURLToFile(new URL(newLoc), new File(System.getProperty("user.home")+filename)); 

使用 copyURLToFile 当前页面不必提供文件。我认为注册类(class)可能是最简单的方法......像这样:

PHP 代码:

<a href='#' onclick="app.getfile('$filename' )">Download $filename</a> 

Java(javafx 类/窗口中的内联类...在本例中,我的 javafx 窗口位于 jframe 内部):

public class JavaApp { 
JFrame cloudFrameREF; 
    JavaApp(JFrame cloudFrameREF) 
    { 
       this.cloudFrameREF = cloudFrameREF;  
    } 
 
    public void getfile(String filename) { 
    String newLoc = "http://your_web_site.com/send_file.php?filename=" + filename; 
    org.apache.commons.io.FileUtils.copyURLToFile(new URL(newLoc), new File(System.getProperty("user.home")+filename)); 
     } 
 } 

这部分将放在主 javafx 类中:

Platform.runLater(new Runnable() { 
        @Override 
        public void run() { 
browser2 = new WebView(); 
webEngine = browser2.getEngine(); 
appREF = new JavaApp(cloudFrame); 
 webEngine.getLoadWorker().stateProperty().addListener( 
        new ChangeListener<State>() { 
          @Override public void changed(ObservableValue ov, State oldState, State newState) { 
 
              if (newState == Worker.State.SUCCEEDED) { 
              JSObject win 
                            = (JSObject) webEngine.executeScript("window"); 
// this next line registers the JavaApp class with the page... you can then call it from javascript using "app.method_name". 
                    win.setMember("app", appREF);   
            } 
 
            } 
        }); 

您可能不需要框架引用...我正在修改自己的一些代码来测试这一点,并且该引用对于其他事情很有用...