PNG元数据读写
PNG metadata read and write
我正在使用 stackover 流程上发布的 code 将自定义元数据写入 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)
您的输出包含 "not found",它是由
创建的
if(pngmeta.getStandardTextNode()==null){
System.out.println("not found");
}
因此
NodeList childNodes = pngmeta.getStandardTextNode().getChildNodes();
必须失败并出现 NullPointerException。 pngmeta.getStandardTextNode() 结果为 null,因此您实际调用
null.getChildNodes();
这是我所知道的修改然后读取元数据的最有效方法。与 OP 发布的代码相比,此版本并未完全替换图像中的元数据,而是将新内容与任何现有内容合并。
因为它使用 "standard" 元数据格式,它也应该适用于 ImageIO 支持的任何格式,允许任意文本注释(不过我只测试了 PNG)。在这种情况下,写入的实际数据应与本机 PNG 元数据格式相匹配。
它使用单一方法读取所有图像像素数据和元数据,以避免过多的流 open/close 以及查找和内存使用。出于同样的原因,它一次写入所有图像像素数据和元数据。对于像 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
我正在使用 stackover 流程上发布的 code 将自定义元数据写入 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)
您的输出包含 "not found",它是由
创建的if(pngmeta.getStandardTextNode()==null){
System.out.println("not found");
}
因此
NodeList childNodes = pngmeta.getStandardTextNode().getChildNodes();
必须失败并出现 NullPointerException。 pngmeta.getStandardTextNode() 结果为 null,因此您实际调用
null.getChildNodes();
这是我所知道的修改然后读取元数据的最有效方法。与 OP 发布的代码相比,此版本并未完全替换图像中的元数据,而是将新内容与任何现有内容合并。
因为它使用 "standard" 元数据格式,它也应该适用于 ImageIO 支持的任何格式,允许任意文本注释(不过我只测试了 PNG)。在这种情况下,写入的实际数据应与本机 PNG 元数据格式相匹配。
它使用单一方法读取所有图像像素数据和元数据,以避免过多的流 open/close 以及查找和内存使用。出于同样的原因,它一次写入所有图像像素数据和元数据。对于像 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