为什么 JavaFX Image 不直接从 FileInputStream 读取文件本身?

Why does a JavaFX Image not read the file itself directly from a FileInputStream?

我正在编写一个交易程序,允许用户上传自己的图片,供其他客户在查看他们发布的列表时查看。请求时从服务器发送图像文件(.png 或 .jpeg 文件)。

似乎当使用 FileInputStream 作为新图像的参数时,尝试打开图像的客户端正在他们自己的计算机上查找该文件(检查它自己的文件系统)而不是直接读取图像文件本身已从服务器发送给它。

在我发布的示例中,我们假设这是 IDE 中的 运行 而不是 JAR。发送文件的服务器程序能够从其 'src' 目录成功访问 "avatar.png"。

我在下面构建了一个最小的可执行示例,由服务器和客户端组成,以表示手头的实际问题。服务器和客户端示例 运行 在本地网络上的不同计算机上。该示例重现了该问题。

来自 FileInputStream 文档:

FileInputStream is meant for reading streams of raw bytes such as image data.

该示例抛出以下异常:

java.io.FileNotFoundException: avatar.png (No such file or directory)

这表明客户端正在其自己的文件系统上寻找 'avatar.png'。

例如服务器:

public class ImageTesterServer {

public static void main(String[] args) {

    ImageTesterServer server = new ImageTesterServer();
    server.sendImage();

}


private void sendImage()
{
    ServerSocket server = null;
    Socket client = null;

    try {
        // Accept client connection, create new File from the 'avatar.png' image, and send to client
        server = new ServerSocket();
        System.out.println("Awaiting client connection...");

        client = server.accept();
        System.out.println("Client connected.");

        File imageFile = new File("avatar.png");
        ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
        oos.writeObject(imageFile);

        System.out.println("Sent image file.");

    } catch (IOException e) {
        e.printStackTrace();
    }
    finally {  // Close sockets
        try {
            if (client != null)
                client.close();
            if (server != null)
                server.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

}

}

客户:

public class ImageTesterClient extends Application {

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

@Override
public void start(Stage primaryStage)
{
    ImageTesterClient client = new ImageTesterClient();
    Image avatar = client.getServerFile();   // Retrieve the image's file from the server
    ImageView picture = new ImageView(avatar);

    StackPane root = new StackPane();
    root.getChildren().add(picture);

    Scene scene = new Scene(root);
    primaryStage.setScene(scene);
    primaryStage.show();
}

private Image getServerFile()
{
    Socket socket = null;
    ObjectInputStream ois;

    try {

        socket = new Socket("192.168.1.147", 5000);   // Open new socket connection to server on another local network computer
        ois = new ObjectInputStream(socket.getInputStream());
        File imageFile = (File)ois.readObject();
        Image avatar = new Image(new FileInputStream(imageFile));
        return avatar;

    } catch (IOException | ClassNotFoundException ex) {
        ex.printStackTrace();
    }
    finally {  // Close the socket if not null
        try {
            if (socket != null)
                socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    return null;
}

}

如何强制客户端图像使用从服务器接收到的文件 (avatar.png) 本身,而不是尝试查看自己的文件系统?

文件 class 不代表文件的 内容。 它只是文件路径的代表。这种混淆是文件 class 被认为已过时(尽管未弃用)并已被 Path class.

取代的众多原因之一

因此,当您执行 oos.writeObject(imageFile); 时,您发送的不是文件的内容,而是该文件的路径。显然,一个文件路径在一台计算机上的存在并不能保证相同的路径在另一台计算机上有效。

您必须单独发送文件的内容。一种方法是打开一个 FileInputStream,将其包装在 BufferedInputStream 中,然后使用 BufferInputStream 的 transferTo 方法:

oos.writeObject(imageFile);
try (InputStream stream =
    new BufferedInputStream(new FileInputStream(imageFile))) {

    stream.transferTo(oos);
}

并且在客户端,使用文件的长度来确定要读取多少字节:

File imageFile = (File) ois.readObject();
long length = imageFile.length();

Path imagePath = Files.createTempFile(null, imageFile.getName());
try (FileChannel imageChannel = FileChannel.open(imagePath, 
    StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {

    imageChannel.transferFrom(Channels.newChannel(ois), 0, length);
}

Image avatar = new Image(imagePath.toUri().toString());