为什么JavaFX的Web引擎在提供没有方案的路径时不显示图像?



JavaFX的WebEngineWebView节点的一部分,它似乎无法显示路径声明为";正常的";文件路径,如/home/user/images/image.png,与传统浏览器不同。然而,当向URI添加一个方案时,它将显示图像,看起来像:file:///home/user/images/image.png

为什么?

以下是我为什么要问这个问题的一些背景:

内存中有一些HTML,其中包含指向磁盘上存储的图像文件的img标记。用于识别这些图像的路径是绝对的,因此采用/home/user/images/image.png的形式。然后使用WebEngineloadContent方法加载该HTML。然而,如上所述,WebEngine将不显示那些图像。因此,我唯一想到的解决方案是遍历HTML并将方案附加到每个路径。但我发现这是一个相当丑陋的解决方案

HTML页面中的相对URL将在页面位置的上下文中解析。因此,如果在浏览器中打开HTML文件,则页面的位置为file:///path/to/file.html。相对于该URL解析的绝对文件路径/home/user/images/image.png将按预期解析为file:///home/user/images/image.png。换句话说,URL的相对分辨率保留了方案file://

在使用loadContent方法将HTML加载到JavaFXWebView的情况下,没有位置(HTML只是在内存中(,因此相对URL的解析将不起作用(没有方案(。

我能想出两个解决办法。需要对HTML进行更改,这可能很方便,也可能不方便。这里的解决方案只是在HTML的<head>中添加一个<base>元素,将file:///指定为文档库。

以下是一个完整的示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
public class HelloApplication extends Application {

@Override
public void start(Stage stage) throws IOException {
FileChooser chooser = new FileChooser();
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
Button browse = new Button("Browse");
browse.setOnAction(e -> chooseImage(chooser, stage, webEngine));
BorderPane root = new BorderPane();
root.setTop(new HBox(5, browse));
root.setCenter(webView);
Scene scene = new Scene(root, 800, 500);
stage.setScene(scene);
stage.show();
}
private void chooseImage(FileChooser chooser, Stage stage, WebEngine webEngine) {
File file = chooser.showOpenDialog(stage);
if (file == null) return;
String imagePath = file.getAbsolutePath();
String html = """
<html>
<head>
<base href="file:///"></base>
</head>
<body>
<div>Image:</div>
<img src="%s"/>
</body>
</html>
""";
html = String.format(html, imagePath);
webEngine.loadContent(html);
}

public static void main(String[] args) {
launch();
}
}

如果由于某种原因无法(或不希望(修改HTML,另一种解决方案是将HTML写入临时文件并从该文件加载。这是使用这种方法的相同示例。您可能想在之后清理文件,这需要一些工作(尽管不太多(。

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
public class HelloApplication extends Application {
private Path tempDir ;
@Override
public void init() {
try {
tempDir = Files.createTempDirectory(Paths.get(System.getProperty("user.home")), ".myApp");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void start(Stage stage) throws IOException {
FileChooser chooser = new FileChooser();
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
Button browse = new Button("Browse");
browse.setOnAction(e -> chooseImage(chooser, stage, webEngine));
BorderPane root = new BorderPane();
root.setTop(new HBox(5, browse));
root.setCenter(webView);
Scene scene = new Scene(root, 800, 500);
stage.setScene(scene);
stage.show();
}
private void chooseImage(FileChooser chooser, Stage stage, WebEngine webEngine) {
File file = chooser.showOpenDialog(stage);
if (file == null) return;
String imagePath = file.getAbsolutePath();
String html = """
<html>
<head>
</head>
<body>
<div>Image:</div>
<img src="%s"/>
</body>
</html>
""";
html = String.format(html, imagePath);
try {
Path tempFile = Files.createTempFile(tempDir, "page", ".html");
Files.writeString(tempFile, html);
String url = tempFile.toUri().toString();
System.out.println(url);
webEngine.load(url);
} catch (IOException e) {
throw new RuntimeException(e);
}
//webEngine.loadContent(html);
}
@Override
public void stop() {
try {
List<Path> files = Files.list(tempDir).collect(Collectors.toList());
for (Path f : files) Files.delete(f);
Files.delete(tempDir);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
launch();
}
}

最新更新