使用 Java nio 写入文件元数据时出现问题

Issues writing file metadata with Java nio

我希望使用 java.nio.file.Files 中的功能向任何类型的文件添加自定义元数据标签。我已经能够正确读取元数据,但每当我尝试设置元数据时都会遇到问题。

我尝试使用 Files.setAttribute 和以下

来设置带有纯字符串的自定义元数据元素
    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();
    }

但最终出现以下错误:

线程 "main" java.lang.ClassCastException 中的异常:java.lang.String 无法转换为 java.nio.ByteBuffer

如果我像这样尝试将该字符串转换为 ByteBuffer

    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();
    }

输出的不是文本'test',而是奇怪的字符串'[B@14e3f41'

将字符串转换为字节缓冲区并将其转换回字符串的正确方法是什么?是否有更可自定义的方法来使用 java 修改文件上的元数据?

用户定义的属性,即UserDefinedFileAttributeView定义的任何属性(前提是你的FileSystem支持它们!),是readable/writable来自Java的字节数组;如果给定属性包含文本内容,则相关字符串的编码取决于进程。

现在,您正在使用 .{get,set}Attribute() 方法,这意味着您有两种选择来编写 user 属性:

  • 或者像你一样使用 ByteBuffer;或者
  • 使用普通字节数组。

您将从中读取的内容始终是字节数组。

来自上面的 javadoc link(强调我的):

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[]).

所以,在你的情况下:

  • 为了写入 属性,从您的字符串中获取具有请求编码的字节数组:

    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);
    
  • 为了读取属性,你需要将.getAttribute()的结果转换为一个字节数组,然后得到一个字符串,再次使用正确的编码:

    final Charset utf8 = StandardCharsets.UTF_8;
    final byte[] userAttributeValue 
        = (byte[]) Files.readAttribute(photo, "user:tags");
    final String myAttrValue = new String(userAttributeValue, utf8);
    

查看其他解决方案,以防万一...

前面已经说过,你要处理的是一个UserDefinedFileAttributeViewFiles class 允许您使用此方法获得任何 FileAttributeView 实现:

final UserDefinedFileAttributeView view
    = Files.getFileAttributeView(photo, UserDefinedFileAttributeView.class);

现在,一旦有了这个视图,您就可以对其进行读取或写入。

例如,这是您读取特定属性的方式;请注意,这里我们只使用属性 name,因为视图(名称 "user")已经存在:

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);

为了写入,您需要将字节数组包装成 ByteBuffer:

final Charset utf8 = StandardCharsets.UTF_8;
final int array = tagValue.getBytes(utf8);
final ByteBuffer buf = ByteBuffer.wrap(array);
view.write("tags", buf);

就像我说的,它给了你更多的控制权,但也更复杂。

最后说明:顾名思义,用户定义的属性是用户定义的;此视图的给定属性可能存在,也可能不存在。如果属性不存在等,您有责任正确处理错误;对于这种情况,JDK 没有提供 NoSuchAttributeException 这样的东西。