你的位置:首页 > Java教程

[Java教程]FileAttributeView出现空指针异常原因分析


问题?     

      Java7新增了关于文件属性信息的一些新特性,通过java.nio.file.*包下面的类可以实现设置或者读取文件的元数据信息(比如最后修改时间,创建时间,文件大小,是否为目录等等)。尤其是UserDefinedFileAttributeView,可以用来自定义文件的元数据信息。于是在自己的mac上写了个小程序测试了下:

 1 import java.io.IOException; 2 import java.nio.ByteBuffer; 3 import java.nio.charset.Charset; 4 import java.nio.file.Files; 5 import java.nio.file.Path; 6 import java.nio.file.Paths; 7 import java.nio.file.attribute.UserDefinedFileAttributeView; 8 import java.util.*; 9 10 public class FileAttributeViewDemo {11   private final static Charset CS = Charset.forName("UTF-8");12 13   public Map<String, String> getAttributes(Path path) throws IOException {14     Map<String, String> map = new HashMap<>();15     Set<String> keys = Files.readAttributes(path, "*").keySet();16     for (String attr : keys) {17       map.put(attr, Files.getAttribute(path, attr).toString());18     }19     return map;20   }21 22   public Map<String, String> getUserMeta(Path path) throws IOException {23     UserDefinedFileAttributeView view = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);24     List<String> metaKeys = view.list();25     Map<String, String> map = new HashMap<>();26     for (String metaKey : metaKeys) {27       ByteBuffer buff = ByteBuffer.allocate(view.size(metaKey));28       view.read(metaKey, buff);29       buff.flip();30       map.put(metaKey, CS.decode(buff).toString());31     }32     return map;33   }34 35   public void writeUserMeta(Path path) {36     UserDefinedFileAttributeView view = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);37     try {38       view.write("author", CS.encode("everSeeker"));39       view.write("date", CS.encode("20160505"));40       view.write("title", CS.encode("Effective-Java中文版.pdf"));41       view.write("pageTotal", CS.encode("229"));42     } catch (IOException e) {43       e.printStackTrace();44     }45   }46 47   public static void main(String[] args) throws IOException {48     FileAttributeViewDemo demo = new FileAttributeViewDemo();49     Path path = Paths.get("/root/xxx/Java/Effective-Java.pdf");50     //读取文件属性信息51     Map<String, String> attrMap = demo.getAttributes(path);52     for (Map.Entry<String, String> entry : attrMap.entrySet()) {53       System.out.println(entry.getKey() + " : " + entry.getValue());54     }55     System.out.println("--------------------------------------------------------------");56     //写自定义文件属性57     demo.writeUserMeta(path);58     //读取自定义文件属性59     Map<String, String> userMetaMap = demo.getUserMeta(path);60     for (Map.Entry<String, String> entry : userMetaMap.entrySet()) {61       System.out.println(entry.getKey() + " : " + entry.getValue());62     }63   }64 }

      整个程序分为3个部分:1、读取文件Effective-Java.pdf文件的元数据信息;2、自定义文件的元数据信息,新增作者,时间,标题,页数这4个文件属性;3、读取自定义的属性信息。

      然后运行了下,就报错了。程序的第38行,view.write("author", CS.encode("everSeeker")); 报空指针错误。通过debug模式查看,发现第36行

UserDefinedFileAttributeView view = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);

定义的view==null。这就很奇怪了,因为这行代码是jdk 8.0 API手册里面的例子。把代码发给同事在window系统上跑了下,一切正常;自己又在kali linux系统上跑了下,也能正常运行。

 

分析:

      1、首先,通过UserDefinedFileAttributeView自定义的文件元数据信息(UserMeta)肯定是持久化了。因为调用一次writeUserMeta(Path path)方法后,重启程序,直接调用getUserMeta(Path path),还是可以获得自定义的元数据信息。所以现在的问题是,这些信息持久化到哪里去了?这个问题在Vamei的博客里面找到了答案。传送门:Linux文件系统的实现。Linux文件存储系统中的inode(索引节点)存储了文件的大小,时间戳,文件权限等信息以及文件数据块block的位置信息。通过命令stat [文件名]可以直接获得inode的相关信息。

      那么,mac系统的文件存储是怎样的呢?通过命令diskutil list发现,我的OSX Yosemite 10.10.5系统的分区格式为Apple_CoreStorage。和Linux系统采取了完全不同的文件系统。所以,基本判断程序异常的原因在于操作系统的区别。

      2、继续研究,发现java7之后的supportedFileAttributeViews方法可以查看本机上面支持的FileAttributeView类型。

1 public void supportedWhichViews() {2     Path path = Paths.get("/Users/xxxx/Books/Java并发编程的艺术.pdf");3     FileSystem fileSystem = path.getFileSystem();4     Set<String> supportedViews = fileSystem.supportedFileAttributeViews();5 6     for (String view : supportedViews) {7       System.out.println(view);8     }9   }

      运行结果为basic, owner, unix, posix;而在Linux系统上运行这段代码,结果为owner, dos, basic, posix, user, unix。可见空指针异常的原因在于在mac osx系统,Java7根本就不支持UserDefinedFileAttributeView这个类。