在 JavaFX 中将剪贴板中的图像编码为 base64

Encode an image from the clipboard as base64 in JavaFX

提问人:Maxim 提问时间:11/17/2023 最后编辑:Maxim 更新时间:11/18/2023 访问量:61

问:

我有一个 JavaFX 应用程序,我试图从剪贴板读取图像并将其编码为 base64 字符串以将其保存在数据库中(最大 100x100 像素的小屏幕截图)。有几个示例可以将图像文件读取为字节数组,但我找不到获取剪贴板图像字节的方法。我试过这个:

var image = Clipboard.getSystemClipboard().getImage();
    if(image != null) {

        int w = (int)image.getWidth();
        int h = (int)image.getHeight();
        byte[] buf = new byte[w * h * 4];

        image.getPixelReader().getPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), buf, 0, w * 4);

        return Base64.getEncoder().encodeToString(buf);
    }

    return null;

但是我从中获得的字符串内容无法以任何格式解码(使用多个在线工具)。

java javafx base64

评论

1赞 jewelsea 11/17/2023
base64 不是图像编码,而是将二进制数据编码为文本。但是图像的二进制数据可以编码为许多不同的格式。您需要的数据的二进制编码格式是什么(例如.jpg,png,tiff,svg,bmp等)?为什么需要将图像的二进制数据编码为 base64?如何使用编码的 base64 数据?作为数据 URL?还是别的什么?请编辑问题进行解释。
0赞 Maxim 11/17/2023
我知道这不是图像编码,但我想要一种轻量级的方法将小屏幕截图保存为数据库中的文本(最大 100x100px)
1赞 jewelsea 11/17/2023
您想要有损存储格式(例如.jpg)还是无损存储格式(例如.png)?任何有能力的数据库都可以在没有 base64 编码的情况下存储二进制数据。这似乎是一个 xy 问题
1赞 jewelsea 11/17/2023
您可能想要做的是获取剪贴板 getImage 调用返回的 JavaFX 图像,将其转换为 BufferedImage,使用 ImageIO 将其编码为 png 或 jpg,然后将其作为 blob 存储在数据库中。然后,JavaFX 从数据库中读取二进制编码的 png 或 jpg,并显示 JavaFX 图像。您不需要 PIxelReader 或 base64 编码/解码。
1赞 Slaw 11/17/2023
请注意,即使您放弃并手动将像素从 “传输”到 ,您仍然可以以相同的方式获得图像文件字节 — 通过 .SwingFXUtilsImageBufferedImageImageIO

答:

3赞 Slaw 11/17/2023 #1

从调用中获取的字节只是原始颜色/像素数据。它不是像 PNG 或 JPEG 这样的图像文件格式。如果您希望其他软件能够解码 Base64 编码的图像,则必须首先将像素编码为实际的图像文件格式。例如:getPixels

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javax.imageio.ImageIO;

public final class ImageUtils {
    
    private ImageUtils() {}
    
    public static byte[] toImageFile(Image image, String format) {
        var out = new ByteArrayOutputStream();
        try {
            ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, out);
        } catch (IOException ex) {
            throw new UncheckedIOException(ex);
        }
        return out.toByteArray();
    }
}

注意:SwingFXUtils 来自 javafx.swing 模块。

一旦你有了图像文件,那么你可以对字节进行 Base64 编码:

var image = Clipboard.getSystemClipboard().getImage();
var bytes = ImageUtils.toImageFile(image, "png");
var base64Str = Base64.getEncoder().encodeToString(bytes);

注意:PNG 是一种无损格式。如果您想要较小的图像文件,则可以使用JPEG等有损格式。

但是你提到将这个图像保存到数据库中。如果您要这样做,那么我建议您只将图像存储为 blob。例如,如果您直接使用 JDBC:

var image = Clipboard.getSystemClipboard().getImage();
var bytes = ImageUtils.toImageFile(image, "png");
try (var conn = DriverManager.getConnection("...")) {
    try (var stat = conn.prepareStatement("INSERT INTO Foo (image) VALUES (?)")) {
        stat.setBinaryStream(1, new ByteArrayInputStream(bytes));
        stat.executeUpdate();
    }
}

如果需要,您当然可以继续对代码的其他部分使用 Base64 编码。

评论

0赞 Maxim 11/17/2023
好的,但我收到此错误:java.lang.ClassNotFoundException: javafx.embed.swing.SwingFXUtils,尽管它在 IDE 中被识别。我还没有使用module-info文件,我必须在那里放一些东西吗?
1赞 jewelsea 11/18/2023
@Maxim 如果您没有 module-info 文件,则您的代码不是模块化的。javafx.swing 是一个模块,即使您的代码不是模块化的,它也需要位于模块路径上。有关为各种 IDE 和 gradle 设置非模块化项目的信息(您在另一条评论中提到过)openjfx.io。这显示了如何将其他 JavaFX 模块(如 javafx.controls)放在模块路径上并使用它们。javafx.swing 模块在设置中没有什么不同,但您确实需要进行设置并为所需模块使用正确的模块名称和路径