問題? Java7新增了關於文件屬性信息的一些新特性,通過java.nio.file.*包下麵的類可以實現設置或者讀取文件的元數據信息(比如最後修改時間,創建時間,文件大小,是否為目錄等等)。尤其是UserDefinedFileAttributeView,可以用來自定義文件的元數據信息。於是在自己的 ...
問題?
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這個類。