如何将图像保存和读取为文本,嵌入到代码中

How to save and read image as text, embedded in the code

我知道这是一个奇怪的问题,但我真的需要这个来解决问题。 我有一个非常小的 .png 图像,我需要将它导入 ("type it") 到 java 源文件而不从文件系统读取。 (至少不是在程序运行时,我可以用其他虚拟应用程序读取系统上的图像来解析是二进制的还是我不知道还有什么)。

换句话说,我确实需要表示图像的代码行,我只会将它们放在那里一次(我不需要即时导入,我只需键入它并永远留在那里)。

图像需要存储为 ImageIcon 并且,由于您可以输入字节数组,我不明白为什么不能创建一个读取图像的小 jar,将字节数组打印到 .txt 文件,然后我可以复制该文件并将其放入源文件中。我只是不知道如何准确地执行这些步骤。

注意: 我知道这 不是 人们会或应该做的事情,它是针对特定项目的,我需要以这种方式完成,原因超出了我的范围.

注 2: 我不能使用任何外部库或依赖项。

"Type" 您的图像为 netpbm 格式,然后将其转换为 PNG 或其他格式。

例如,我正在使用 SE markdown 编辑器创建此图像:

P3 3 3 255
0 0 0  0 0 0    0 0 0 
0 0 0  0 255 0  0 0 0
0 0 0  0 0 0    0 0 0

这是一个 3x3 的图像,由一个黑色框包围着一个绿色像素。

另存为"image.ppm"。 使用 ImageMagick 将其转换为 PNG(将其放大 20 倍以便您可以看到):

magick convert image.ppm -sample 2000% image.png

(此处您需要将 image.ppm 转换为您的 ImageIcon 格式)

显示它:

您不想 "type" 直接转换为 PNG 格式,因为它很奇怪:

data:image/png;base64,
UDMgMyAzIDI1NQogICAgMCAwIDAgIDAgMCAwICAgIDAgMCAwIAogICAgMCAwIDAgIDAgMjU1IDAg
IDAgMCAwCiAgICAwIDAgMCAgMCAwIDAgICAgMCAwIDAK

如果您从 PNG 开始并想要 "type" 它,您可以再次使用 ImageMagick,但方向相反。

例如,把你的头像转换成mark.ppm

magick convert 8ede7*.png -compress none mark.ppm
cat mark.ppm
P3 32 32 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 232 136 234 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 229 119
231 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 239 174 241 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 232 136 234 207 1 211 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 207 1 211 229 119 231 255 255 255 255 255 255 255 255
[...]

如果你能处理"C"风格headers,你可以试试

 magick 8ede7*.png mark.gif
 magick mark.gif mark.h
 cat mark.h
  mark.h (GIF).
*/
static unsigned char
  MagickImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x20, 0x00, 0x20, 0x00, 0xF3, 0x0E,
    0x00, 0xCF, 0x01, 0xD3, 0xD7, 0x30, 0xDB, 0xD9, 0x38, 0xDC, 0xDA, 0x40,
    0xDD, 0xDC, 0x49, 0xDF, 0xE2, 0x67, 0xE4, 0xE5, 0x77, 0xE7, 0xE8, 0x88,
    0xEA, 0xEF, 0xAE, 0xF1, 0xF1, 0xB6, 0xF2, 0xF3, 0xBF, 0xF4, 0xF4, 0xC7,
[...]

仅供参考,与此类似的问题可能有助于提供更深入的 explanation/answer here and here

我的回答是为了解决您的问题,

I literally need lines of code that represent the image, which I will only put there once (I don't need to import on the fly, I just type it and leave it there forever).

是使用Base64,读取指定格式的图片代码。

例如,下面的代码使一个简单的 JFrame 将 base64 字符串显示为图像图标:

import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Scanner;

import javax.imageio.ImageIO;
import javax.swing.*;

public class App extends JFrame {
    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                App app = new App();
                app.setVisible(true);
            }
        });
    }
    public App() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel photo = new JLabel();
        photo.setVerticalTextPosition(JLabel.BOTTOM);
        photo.setHorizontalTextPosition(JLabel.CENTER);
        photo.setHorizontalAlignment(JLabel.CENTER);
        photo.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
        add(photo, BorderLayout.CENTER);
        setSize(200, 100);
        setLocationRelativeTo(null);

        // Ex. Whosebug.ico (then converted to png for this example)
        String imgBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAJ+SURBVFhH7ZXbS1RRFMb7Y5qHiu4PEfUQRS9B9FBEBBVFT70WvQQhmJpdLUkMS0mNikDKlKxIDR+iCz3YmJgWCZmlKWPNPvd9vvZeZ8+ZS3NmzlzoYn2wHmatYe/f2etbey/Ab9Z/gPkFwCcGoTVtB/8yojL5VTYA1/gGdnIV4hULoV3cBFefU5XcKusJ2G96BECEIPT2A4LKUZVglQYgNuBTo+qHJ7P3PAHIMB+dUtlglQRgdh1HvHIx7Jc3VEbI5dCvH1QQEdjRblXIrqIB3NhHsDNr/a81Oo4AlubVhB+0+s1erWop+OdhymdTSSfgfp+C3rLbh9AatoB/fU81Pv0OrGYl5VndBrhslvKZKt2E3KFeJ8zHalbAHrpPJWfksZ/XW/eSZzJVMIA92CkW7qVep8oZfuiPodzU7KkScDas/nqVE6aUuQwVBODN+mpaTLuwEdbTFsolxGc+QGvc6m+oN++COzcJ/eYhsOpl6WZVKghAjpxcNLGBDFa9HGZ3hd972AaMO0eT9dNr4IwNgM+Oe/UMFeUB/inqbSJGMAkTocvHGe2n9tivbiN+YgnYufV0CkEqyYQum4H15BLY2XUpIKI9YgStZ61wxl/kfRdCAzhv+4QB74rxGvvZzcJs9ut70K7sSAOxBhrVH4IVGkC/tie5uLhc9Ks7qffyqPnkEEFIyRfR6DhMl5RrximXS+EB2vbTjKd+YVpULoJ2eRuMzmOwnrfTRIRRYR4Q5pJut6Nd4vKpFZfLPrBabywzQ7YsjAIBmttuUYSRG5sQF9EDmH119BCR87UY1fKtUxaAXCobQLZjzhUJzR+AYvX3A5QrgvTnAvwq/esAwA/q0/BiaJrlDgAAAABJRU5ErkJggg==";
        String data = imgBase64.substring(imgBase64.indexOf(",") + 1);
        ByteArrayInputStream stream = new ByteArrayInputStream(Base64.getDecoder().decode(data.getBytes()));
        BufferedImage image = null;
        try {
            image = ImageIO.read(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ImageIcon imgIcon = new ImageIcon(image.getScaledInstance(32, 32, 0));
        photographLabel.setIcon(imgIcon);
    }
}

此代码不使用任何外部库并完成您所请求的大部分任务 - 因此,为了完全完成答案,下面是我将如何从控制台读取该输入(也就是用户键入控制台)。注意:以下部分代码将添加到 ByteArrayInputStream 流对象声明行之后。

Scanner sc = new Scanner(System.in);
System.out.println("Type .png input (base64): ");
StringBuilder sb = new StringBuilder();
while (sc.hasNext())
    sb.append(sc.next());
String data = sb.toString();

这基本上会替换前面示例代码段中的 'String imgBase64' 和 'String data' 两行。

希望这能回答您的问题,请提供反馈以进一步说明或标记为答案;)

干杯

编辑: 从图像到字符串,图像 -> 字符串: 注意:我在此示例中使用的是文件,您也可以选择查看 URL 或 Stream 选项,它们可以在 ImageIO.read() 方法中交替使用。 另请注意:try-with-resources 是 java 1.7+(如果早期版本需要,可以轻松恢复为 try-catch)

假设您从某处加载了一个 ImageIcon 对象,URL/stream/file,您可以命名。在我的示例中,我将使用本地文件:

ImageIcon icon = new ImageIcon("C:\Users\Nick\Desktop\favicon.png");

现在拿这个图片图标,得到你要的base64字符串?这是一种方法/我是如何做到的(使用已经加载的 ImageIcon 图标对象):

try (ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageOutputStream ios = ImageIO.createImageOutputStream(os)) {
    BufferedImage img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.BITMASK);
    Graphics g = img.createGraphics();
    icon.paintIcon(null, g, 0, 0);
    g.dispose();
    ImageIO.write(img, "png", ios);
    byte[] bytes = os.toByteArray();
    String data = Base64.getEncoder().encodeToString(bytes);
    System.out.println(data);
} catch (IOException e) {
    e.printStackTrace();
}

我想指出的一件事是使用了 'BufferedImage.BITMASK' 标志。这很容易混淆或切换,并导致与 decoded/encoded 字符串值不一致。

更多关于 ImageIcon class 的信息:https://docs.oracle.com/javase/7/docs/api/javax/swing/ImageIcon.html

使用此工具将 Whosebug 图标转换为 base64 https://www.base64-image.de/