java - PNG元数据读取和写入

标签 java png metadata javax.imageio

我正在使用一 block code发布在 stackover flow 上,将自定义元数据写入 PNG 图像并读取它。写入函数似乎工作正常,但是当我尝试读取我写入的数据时,它会抛出 NullPointerException。有人可以告诉我出了什么问题吗?

这是编写元数据的代码

try{
    image=ImageIO.read(new FileInputStream("input.png"));
    writeCustomData(image, "software", "FRDDC");
    ImageIO.write(image, "png", new File("output.png"));
    }
    catch(Exception e){
    e.printStackTrace();
    }

写入元数据的方法

   public static byte[] writeCustomData(BufferedImage buffImg, String key, String value) throws Exception {
    ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();

    ImageWriteParam writeParam = writer.getDefaultWriteParam();
    ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);

    //adding metadata
        javax.imageio.metadata.IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);

    IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry");
    textEntry.setAttribute("keyword", key);
    textEntry.setAttribute("value", value);

    IIOMetadataNode text = new IIOMetadataNode("tEXt");
    text.appendChild(textEntry);

    IIOMetadataNode root = new IIOMetadataNode("javax_imageio_png_1.0");
    root.appendChild(text);

    metadata.mergeTree("javax_imageio_png_1.0", root);

    //writing the data
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
        javax.imageio.stream.ImageOutputStream stream = ImageIO.createImageOutputStream(baos);
    writer.setOutput(stream);
    writer.write(metadata, new IIOImage(buffImg, null, metadata), writeParam);

    try {

            ImageIO.write(buffImg, "png", new File("new.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    stream.close();

    return baos.toByteArray();
}

读取元数据

try{
image=ImageIO.read(new FileInputStream("output.png"));

            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            ImageIO.write(image, "png", baos );
            byte[] b=baos.toByteArray();
            String out=readCustomData(b, "software");
}
catch(Exception e){
e.printStackTrace();
}

读取元数据的方法

 public static String readCustomData(byte[] imageData, String key) throws IOException{
    ImageReader imageReader = ImageIO.getImageReadersByFormatName("png").next();

    imageReader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(imageData)), true);

    // read metadata of first image
        javax.imageio.metadata.IIOMetadata metadata = imageReader.getImageMetadata(0);

    //this cast helps getting the contents

     //Node n=metadata.getAsTree("javax_imageio_png_1.0");
     //NodeList childNodes=n.getChildNodes();
    PNGMetadata pngmeta = (PNGMetadata) metadata; 
    if(pngmeta.getStandardTextNode()==null){
        System.out.println("not found");
    }
    NodeList childNodes = pngmeta.getStandardTextNode().getChildNodes();

    for (int i = 0; i < childNodes.getLength(); i++) {
        Node node = childNodes.item(i);
        String keyword = node.getAttributes().getNamedItem("keyword").getNodeValue();
        String value = node.getAttributes().getNamedItem("value").getNodeValue();
        if(key.equals(keyword)){
            return value;
        }
    }
    return null;
}

错误信息

not found
java.lang.NullPointerException
    at PNGMeta.readCustomData(PNGMeta.java:104)
    at PNGMeta.main(PNGMeta.java:40)
BUILD SUCCESSFUL (total time: 2 seconds)

最佳答案

这是我所知道的修改然后读取元数据的最有效方法。与 OP 发布的代码相比,此版本并未完全替换图像中的元数据,而是将新内容与任何现有内容合并。

由于它使用“标准”元数据格式,因此它也应该适用于 ImageIO 支持的任何格式,允许任意文本注释(不过我只测试了 PNG)。在这种情况下,实际写入的数据应与 native PNG 元数据格式匹配。

它使用单一方法读取所有图像像素数据和元数据,以避免过多的流打开/关闭、查找和内存使用。出于同样的原因,它会一次写入所有图像像素数据和元数据。对于无损的单一图像格式(例如 PNG),此往返不应丢失任何质量或元数据。

读回元数据时,仅读取元数据,忽略像素数据。

public class IIOMetadataUpdater {

    public static void main(final String[] args) throws IOException {
        File in = new File(args[0]);
        File out = new File(in.getParent(), createOutputName(in));

        System.out.println("Output path: " + out.getAbsolutePath());

        try (ImageInputStream input = ImageIO.createImageInputStream(in);
             ImageOutputStream output = ImageIO.createImageOutputStream(out)) {

            Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
            ImageReader reader = readers.next(); // TODO: Validate that there are readers

            reader.setInput(input);
            IIOImage image = reader.readAll(0, null);

            addTextEntry(image.getMetadata(), "foo", "bar");

            ImageWriter writer = ImageIO.getImageWriter(reader); // TODO: Validate that there are writers
            writer.setOutput(output);
            writer.write(image);
        }

        try (ImageInputStream input = ImageIO.createImageInputStream(out)) {
            Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
            ImageReader reader = readers.next(); // TODO: Validate that there are readers

            reader.setInput(input);
            String value = getTextEntry(reader.getImageMetadata(0), "foo");

            System.out.println("value: " + value);
        }
    }

    private static String createOutputName(final File file) {
        String name = file.getName();
        int dotIndex = name.lastIndexOf('.');

        String baseName = name.substring(0, dotIndex);
        String extension = name.substring(dotIndex);

        return baseName + "_copy" + extension;
    }

    private static void addTextEntry(final IIOMetadata metadata, final String key, final String value) throws IIOInvalidTreeException {
        IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
        textEntry.setAttribute("keyword", key);
        textEntry.setAttribute("value", value);

        IIOMetadataNode text = new IIOMetadataNode("Text");
        text.appendChild(textEntry);

        IIOMetadataNode root = new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName);
        root.appendChild(text);

        metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, root);
    }

    private static String getTextEntry(final IIOMetadata metadata, final String key) {
        IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
        NodeList entries = root.getElementsByTagName("TextEntry");

        for (int i = 0; i < entries.getLength(); i++) {
            IIOMetadataNode node = (IIOMetadataNode) entries.item(i);
            if (node.getAttribute("keyword").equals(key)) {
                return node.getAttribute("value");
            }
        }

        return null;
    }
}

上述代码的预期输出是:

Output path: /path/to/yourfile_copy.png
value: bar

关于java - PNG元数据读取和写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41265608/

相关文章:

java - 避免 Java 8 Files.walk(..) 终止原因 ( java.nio.file.AccessDeniedException )

android - 如何在 Canvas 中使用部分图像

python - 以编程方式将 png 图像插入 pdf 文件中的特定位置

java - 我想安排一个任务来使用 Java 代码每周运行一个批处理文件

java - Tomcat 7 + JNDI 资源 + IBM WebSphere MQ

java - 测试文件是否存在但出现空指针异常

Android Png 覆盖问题

java - 需要 Java 中文件的内容创建日期

c++ - 使用 ffmpeg 添加元数据信息

mysql - Magento 2重命名表元数据锁