I'm looking to add a custom metadata tag to any type of file using functionality from java.nio.file.Files. I have been able to read metadata correctly, but am having issues whenever I try to set metadata.
I've tried to set a custom metadata element with a plain string using Files.setAttribute with the following
Path photo = Paths.get("C:\\Users\\some\\picture\\path\\2634.jpeg");
try{
BasicFileAttributes attrs = Files.readAttributes(photo, BasicFileAttributes.class);
Files.setAttribute(photo, "user:tags", "test");
String attribute = Files.getAttribute(photo, "user:tags").toString();
System.out.println(attribute);
}
catch (IOException ioex){
ioex.printStackTrace();
}
but end up with the following error :
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.nio.ByteBuffer
if I try to cast that string to a ByteBuffer like so
Path photo = Paths.get("C:\\Users\\some\\picture\\path\\2634.jpeg");
try{
BasicFileAttributes attrs = Files.readAttributes(photo, BasicFileAttributes.class);
Files.setAttribute(photo, "user:tags", ByteBuffer.wrap(("test").getBytes("UTF-8")));
String attribute = Files.getAttribute(photo, "user:tags").toString();
System.out.println(attribute);
}
catch (IOException ioex){
ioex.printStackTrace();
}
instead of outputting the text 'test', it outputs the strange character string '[B@14e3f41'
What is the proper way to convert a String to a bytebuffer and have it be convertable back into a string, and is there a more customizable way to modify metadata on a File using java?
User defined attributes, that is any attribute defined by UserDefinedFileAttributeView
(provided that your FileSystem
supports them!), are readable/writable from Java as byte arrays; if a given attribute contains text content, it is then process dependent what the encoding will be for the string in question.
Now, you are using the .{get,set}Attribute()
methods, which means that you have two options to write user
attributes:
ByteBuffer
like you did; orWhat you will read out of it however is a byte array, always.
From the javadoc link above (emphasis mine):
Where dynamic access to file attributes is required, the getAttribute method may be used to read the attribute value. The attribute value is returned as a byte array (byte[]). The setAttribute method may be used to write the value of a user-defined attribute from a buffer (as if by invoking the write method), or byte array (byte[]).
So, in your case:
in order to write the attribute, obtain a byte array with the requested encoding from your string:
final Charset utf8 = StandardCharsets.UTF_8;
final String myAttrValue = "Mémé dans les orties";
final byte[] userAttributeValue = myAttrValue.getBytes(utf8);
Files.setAttribute(photo, "user:tags", userAttributeValue);
in order to read the attribute, you'll need to cast the result of .getAttribute()
to a byte array, and then obtain a string out of it, again using the correct encoding:
final Charset utf8 = StandardCharsets.UTF_8;
final byte[] userAttributeValue
= (byte[]) Files.readAttribute(photo, "user:tags");
final String myAttrValue = new String(userAttributeValue, utf8);
A peek into the other solution, just in case...
As already mentioned, what you want to deal with is a UserDefinedFileAttributeView
. The Files
class allows you to obtain any FileAttributeView
implementation using this method:
final UserDefinedFileAttributeView view
= Files.getFileAttributeView(photo, UserDefinedFileAttributeView.class);
Now, once you have this view at your disposal, you may read from, or write to, it.
For instance, here is how you would read your particular attribute; note that here we only use the attribute name, since the view (with name "user"
) is already there:
final Charset utf8 = StandardCharsets.UTF_8;
final int attrSize = view.size("tags");
final ByteBuffer buf = ByteBuffer.allocate(attrSize);
view.read("tags", buf);
return new String(buf.array(), utf8);
In order to write, you'll need to wrap the byte array into a ByteBuffer
:
final Charset utf8 = StandardCharsets.UTF_8;
final int array = tagValue.getBytes(utf8);
final ByteBuffer buf = ByteBuffer.wrap(array);
view.write("tags", buf);
Like I said, it gives you more control, but is more involved.
Final note: as the name pretty much dictates, user defined attributes are user defined; a given attribute for this view may, or may not, exist. It is your responsibility to correctly handle errors if an attribute does not exist etc; the JDK offers no such thing as NoSuchAttributeException
for this kind of scenario.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With