文件操作
上一篇已經總結了流操作,其中也包括文件的讀寫。文件系統除了讀寫以為還有很多其他的操作,如復制、移動、刪除、目錄瀏覽、屬性讀寫等。在Java7之前,一直使用File類用於文件的操作。Java7提供了Path,Paths,Files等類,使文件操作變得簡單和全面。此外還有很多第三方庫也提供了文件操作的便捷類如common.io中的FileUtils類,Ant api提供的FileSet等類。
1.File類的使用
Java7之前版本中,File類即代表了路徑對象也封裝了文件的絕大部分操作。
File
- File(String pathname)
- File(String parent, String child)
- File(File parent, String child)
- File(URI uri)
- URI toURI()
- Path toPath()
- String getPath()/toString()
- String getName() 最后一段
- String getParent()
- File getParentFile()
- String getAbsolutePath()
- File getAbsoluteFile()
- String getCanonicalPath()
- File getCanonicalFile()
- boolean isAbsolute()
- boolean isFile()
- boolean isDirectoy()
- boolean exists()
- long length()
- boolean createNewFile()
- boolean delete()
- void deleteOnExit()
- boolean mkdir()
- boolean mkdirs()
- boolean renameTo(File dst)
- File createTempFile(String prefix, String suffix, File directory)
- File createTempFile(String prefix, String suffix)
- String[] list()
- String[] list(FilenameFilter filter)
- File[] listFiles()
- File[] listFiles(FilenameFilter filter)
- File[] listFiles(FileFilter filter)
- boolean isHidden()
- long lastModified()
- boolean canRead()
- boolean canWrite()
- boolean canExecute()
- boolean setLastModified(long time)
- boolean setReadOnly()
- boolean setWritable(boolean writable, boolean ownerOnly)
- boolean setWritable(boolean writable)
- boolean setReadable(boolean readable, boolean ownerOnly)
- boolean setReadable(boolean readable)
- boolean setExecutable(boolean executable, boolean ownerOnly)
- boolean setExecutable(boolean executable)
- long getTotalSpace() 返回文件所在分區的總大小
- long getFreeSpace()
- long getUsableSpace()
2.Path接口和Files類的使用
Java7中提供了Path接口代表了文件系統路徑對象,而Files類封裝了文件系統的絕大部分操作。
基本用法
Paths 使用默認的FileSystem類生成Path對象
- static Path get(String first, String... more) 拼出路徑
- static Path get(URI uri) 將URI對象轉化成Path對象
File類的Path toPath()方法也可以將File對象轉化成Path對象。
Path 路徑對象
- File toFile()
- URI toUri()
- String toString()
- Path normalize() 移除., ..等冗余路徑元素
- Path toAbsolutePath() 返回等價的絕對路徑
- Path relativize(Path other) 返回相對於other的相對路徑
- Path getParent() 返回父路徑
- Path getFileName() 返回路徑最后一個部件
- Path getRoot() 返回根路徑
- Path resolve(Path/String other) 如果other為絕對路徑,返回other;否則,將other接在this路徑的后面返回。
- Path resolveSibling(Path/String other) 如果other為絕對路徑,返回other;否則,將other接在this的父路徑后面返回。
- boolean startsWith(Path/String other)
- boolean endsWith(Path/String other)
files類方法中涉及的Option選項用的是不定參數,同時使用多個選項的方式為:
Files.copy(src, dst, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
Files
- static byte[] readAllBytes(Path path) 不太適合大文件
- static List<String> readAllLines(Path path, Charset cs) 不太適合大文件
- static Path write(Path path, byte[] bytes, OpenOption...options) 如StandardOpenOption.APPEND|READ|WRITE....
- static Path write(Path path, Iterable<? extends CharSequence>, Charset cs, OpenOption... options)
- static InputStream newInputStream(Path p, OpenOption...options) 將文件打開為輸入流
- static OutputStream newOutputStream(Path p, OpenOption...options) 將文件打開為輸出流
- static BufferedReader newBufferedReader(Path path, Charset cs)
- static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption...options)
- static Path copy(Path src, Path dst, CopyOption...options) 如StandardCopyOption.REPLACE_EXISTING|COPY_ATTRIBUTES|ATOMIC_MOVE
- static Path copy(Path src, OutputStream os )
- static Path copy(InputStream src, Path dst, CopyOption...options)
- static Path move(Path src, Path dst, CopyOption...options)
- static void delete(Path path) 刪除的文件不存在會拋出異常,下面這個方法更省事
- static boolean deleteIfExists(Path path)
- static Path createDirectory(Path dir, FileAttribute<?> ...attrs) 不會自動創建中間目錄
- static Path createDirectorys(Path dir, FileAttribute<?> ...attrs) 自動創建中間目錄
- static Path createFile(Path path, FileAttribute<?> ...attrs) 不會自動創建中間目錄,文件已經存在也會拋出異常,檢查文件存在和創建文件是原子操作,其他程序在這個過程中也無法創建文件。
- static Path createTempFile(Path path, String prefix, String suffix, FileAttribute<?>...attr) 創建臨時文件,可以固定文件名前綴或(和)后綴,中間部分隨機生成。未指定Path的重載方法會自動在適當的臨時文件目錄中創建臨時文件。
- static long size(Path path) 文件字節數
- static boolean exists(Path path, LinkOption...option)
- static boolean noExists(Path path, LinkOption...option)
- static boolean isHidden(Path path)
- static boolean isReadable(Path path)
- static boolean isWritable(Path path)
- static boolean isExecutable(Path path)
- static boolean isRegularFile(Path path, LinkOption...option)
- static boolean isDirectory(Path path, LinkOption...option)
- static boolean isSymbolicLink(Path path)
- static UserPrincipal getOwner(Path path, LinkOption...option)
- static Path setOwner(Path path, UserPrincipal owner)
- static Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption...option)
- static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms)
- static FileTime getLastModifiedTime(Path path, LinkOption...option)
- static Path setLastModifiedTime(Path path, FileTime time)
- static <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
- static Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)
- static Object getAttribute(Path path, String attribute, LinkOption...options)
- static Path setAttribute(Path path, String attribute, Object value, LinkOption...options)
- static DirectoryStream<Path> newDirectoryStream(Path path) 生成的DirectoryStream類實現了Iterable<T>接口,可以用來迭代目錄內容
- static DirectorySteram<Path> newDirectoryStream(Path path, String glob) 生成的DirectoryStream可以迭代與glob模式匹配的目錄內容(glob模式見后面)
- static DirectoryStream<Path> newDirectoryStream(Path path, Filter<? super Path> filter) 生成的DirectoryStream可以迭代與過濾器匹配的目錄內容
- static Path walkFileTree(Path path, FileVisitor<? super Path> visitor) 遍歷目錄內容
- static Path walkFileTree(Path path, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor)
文件屬性
通過讀取文件屬性對象可以獲取文件的各種屬性信息(直接使用Files類的一些方法也可以獲取或設置部分屬性信息)。
BasicFileAttributes接口描述了文件的通用屬性(FileTime類型的創建/最后一次訪問/最后一次修改時間,常規文件/目錄/符號鏈接,文件大小,文件主鍵),通用屬性獲取方式為: BasicFileAttributes attributes=Files.readAttributes(path, BasicFileAttributes.class);
Posix兼容文件系統還可以獲取Posix屬性:PosixFileAttributes attributes=Files.readAttributes(path, PosixFileAttributes.class);
PosixFilePermission為枚舉類型,對應Posix的9種權限。
BasicFileAttributes
- FileTime creationTime()
- FileTime lastAccessTime()
- FileTime lastModifiedTime()
- boolean isRegularFile()
- boolean isDirectory()
- boolean isSymoblicLink()
- boolean isOther() 上面三種類型都不是
- long size()
- Object fileKey() 文件唯一標識
PosixFileAttributes implements BasicFileAttributes
- UserPrincipal owner()
- UserPrincipal group()
- Set<PosixFilePermission> permissions()
目錄迭代
File類的list方法在目錄包含大量的文件時效率較低,Files提供了改進的方法Directory newDirectoryStream(Path dir) 可以迭代當前目錄中的對象(不包含子目錄中對象)。該方法還有一個支持glob模式的重載和一個支持過濾器的重載。
Glob模式
- * 當前目錄中匹配0個或多個字符
- ** 所有子目錄中匹配0個或多個字符
- ? 匹配一個字符
- [...] 匹配一個字符集合,可用連字符 - 和取反符 ' ,如[0-9A-Za-z], ['0-9]
- {.. , ..} 匹配由 , 分隔的任意選項
- \ 轉義glob特殊字符
- DirectoryStream<Path> iter=Files.newDirectoryStream(Paths.get("H://zipTest"));
- DirectoryStream<Path> iter1=Files.newDirectoryStream(Paths.get("H://zipTest"),"*.txt");
- DirectoryStream<Path> iter2=Files.newDirectoryStream(Paths.get("H://zipTest"),new DirectoryStream.Filter<Path>(){
- @Override
- public boolean accept(Path entry) throws IOException {
- return !Files.isDirectory(entry);
- }});
- System.out.println("---all---");
- for(Path p: iter)
- System.out.println(p.toString());
- System.out.println("---*.txt---");
- for(Path p: iter1)
- System.out.println(p.toString());
- System.out.println("---only file---");
- for(Path p: iter2)
- System.out.println(p.toString());
目錄遍歷
遍歷目錄中以及所有子目錄中所有項目,使用walkFileTree方法,該方法需要傳入一個FileVisitor接口的對象。SimpleFileVisitor是一個實現了該接口的便捷類,該類中visitFileFailed方法會拋出異常終止遍歷,其他方法什么都不做。
FileVisitor<T>
- FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 一個目錄被處理前被調用
- FileVisitResult postVisitDirectory(T dir, IOException exc) 一個目錄被處理后被調用
- FileVisitResult visitFile(T file, BasicFileAttributes attrs) 遇到一個文件時調用(T為Path)
- FileVisitResult visitFileFailed(T file, IOException exc) 訪問文件發生錯誤時調用,如無權限
walkFileTree遍歷時會讀取項目的屬性用來判斷該項目是目錄還是文件,讀取到的屬性值就順便傳給FileVisitor的方法,FileVisitor的方法里就不需要手動去讀屬性了。
FileVisitor的每個方法都返回FileVisitResult枚舉,指示如何進行后續操作。
FileVisitorResult
- CONTINUE 繼續訪問下一個項目
- TERMINATE 終止遍歷
- SKIP_SUBTREE 跳過這個目錄下的所有子項,繼續遍歷
- SKIP_SIBLINGS 跳過這個目錄的所有平級兄弟項目,繼續遍歷
- Files.walkFileTree(Paths.get("H://zipTest"), new SimpleFileVisitor<Path>()
- {
- @Override
- public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
- {
- System.out.println(dir.toString());
- return FileVisitResult.CONTINUE;
- }
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
- {
- System.out.println("\t"+file.toString());
- return FileVisitResult.CONTINUE;
- }
- @Override
- public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
- {
- return FileVisitResult.CONTINUE;
- }
- });
zip文件系統
Path、Files等類默認都是使用的當前操作系統的磁盤文件系統類型,也可以使用其他類型的文件系統。
FileSystems.newFileSystem(xxx)方法可以創建一個文件內的文件系統,然后使用和普通文件系統一樣的方式操作該文件的內容。 (基於一個文件創建文件系統時會遍歷 FileSystemProvider的installedProviders方法返回的所有FileSystemProvider,查找一個能創建該文件系統的FileSystemProvider,沒有找到的話還查找ClassLoader能加載的文件系統...)
直接用Paths.get(xxx)方法獲取是在默認文件系統里的路徑(等價於FileSystems.getDefault().getPath(xxx); ),而使用FileSystem對象的getPath()方法獲取的路徑就是該文件系統中的路徑。
- FileSystem fs=FileSystems.newFileSystem(Paths.get("ziptest.zip"), null);
- Path copyFile=Paths.get("copyfile.txt"); Files.deleteIfExists(copyFile);
- Files.copy(fs.getPath("textfile.txt"), copyFile);
- Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor<Path>(){
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
- {
- System.out.println(file.toString());
- return FileVisitResult.CONTINUE;
- }
- }); fs.close();
3.底層的文件系統
FileSystems
FileSystem
留坑待填...
內存映射文件
內存映射文件只指將整個文件或者文件的一部分映射到內存中,這個文件就可以像內存數組一樣訪問。內存映射速度比帶緩存的流還要快一點(帶緩沖的流可以極大提高讀寫速度)。內存映射適用於需要隨機訪問大文件,對應順序對入的中小文件沒必要使用。
內存映射文件會用到FileChannel類,對文件的讀寫都轉化成了對內存緩沖區的操作。內存映射文件的操作:首先是建立文件通道,然后調用map方法獲取映射的內存緩沖區,最后操作內存緩沖區即可(緩沖區中的更改會在適當的時候和通道關閉時寫回存儲設備)。
FileChannel類是對磁盤文件的一種抽象,讀寫操作都是與緩沖區交互,主要用在內存映射,文件加鎖機制,以及文件間快速數據傳遞等操作系統特性。
FileChannel
- static FileChannel open(Path path, OpenOption... options) 也使用Files類介紹中提到的StandardOpenOption
- static FileChannel open(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
- long size() 文件大小
- long position() 返回文件指針位置
- void position(long pos) 設置文件指針位置
- void force(boolean metaData) 強制將文件的變更同步到存儲設備
- FileChannel truncate(long size) 截斷
- int read(ByteBuffer dst)... 從通道讀入內容到緩沖區
- int write(ByteBuffer src)... 將緩沖區內容寫入通道
- long transferTo(xxx)
- long transferFrom(xxx)
- MappedByteBuffer map(MapMode, long startposition, long size) MapMode.READ_ONLY|READ_WRITE|PRIVATE
- void close() 關閉通道,如果有可寫的內存映射,其中的更改會寫回存儲設備
FileInputStream
- FileChannel getChannel()
FileOutputStream
- FileChannel getChannel()
RandomAccessFile
- FileChannel getChannel()
- CRC32 crc=new CRC32();
- CRC32 crc1=new CRC32();
- FileChannel fc=FileChannel.open(Paths.get("copyfile.txt"),StandardOpenOption.READ,StandardOpenOption.WRITE);
- MappedByteBuffer buffer=fc.map(MapMode.READ_WRITE, 0, fc.size());
- //隨機訪問緩沖區
- for(int p=0; p<buffer.limit(); p++)
- {
- int c=buffer.get(p);
- crc.update(c);
- }
- //順序訪問緩沖區
- for(int p=0; p<buffer.limit(); p++)
- {
- int c=buffer.get();
- crc1.update(c);
- }
- System.out.println(Long.toHexString(crc.getValue()));
- System.out.println(Long.toHexString(crc1.getValue()));
內存緩沖區
前面的例子已經用到了緩沖區類,現在詳述總結一下這些類。緩沖區類其實就是相同類型數據構成的一個數組,再加上對緩沖區操作的一些方法。
緩沖區類的共同特點:
- 都有一個固定的容量,不可改變。
- 有一個讀寫位置,下一個值在此位置讀寫。
- 有一個界限,超過它之后讀寫就沒有意義。
- 一個可選的標記位置,可以移動到該位置重復讀入或寫出。
- 各個位置的關系為:0<=標記<=讀寫位置<=界限<=容量
- 寫和讀操作在緩沖區上反復交替進行,緩沖區被循環使用。
緩沖區類一般都使用靜態的allocate()方法創建或者使用warp()方法將一個數組包裝成緩沖區對象。
使用過程:
- 重置:調用clear()方法,緩沖區的位置=0,界限=容量。
- 寫入:然后向緩沖區寫入數據,直到緩沖區位置=容量(占滿)或者數據已經寫完。
- 反轉:調用flip()方法,緩沖區界限=位置,位置=0
- 讀取:從緩沖區讀取數據,直到位置=界限。然后再重置,一直循環...
讀寫的過程中可以調用rewind()將位置重置為0,重頭讀寫。或者調用mark()/reset()使用標記,重新讀寫部分內容。
java中緩沖區都繼承自Buffer抽象類,繼承關系如下(不包括StringBuffer)。
Buffer
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
Buffer
- Buffer clear() 重置(讀寫位置=0,界限=容量),准備寫入
- Buffer flip() 反轉(界限=讀寫位置,新讀寫位置=0),准備讀取
- Buffer rewind() 界限不變,讀寫位置=0,可以重頭讀寫
- Buffer mark() 在當前讀寫位置設置標記
- Buffer reset() 重置讀寫位置到標記,可以重新標記后的內容
- int remaining() 剩余可讀或寫的數量,即 limit-position
- boolean hasRemaining() 是否還可讀或可寫。即 position<limit ?
- int position() 返回當前的讀寫位置
- Buffer position(int newpos) 改變讀寫位置
- int capacity() 返回緩沖區容量
- int limit() 返回限制位置
- Buffer limit(int limit) 設置限制位置
- boolean hasArray() 該Buffer是否是包裝了一個數組
- Object array() 返回該Buffer包裝的數組
- int arrayOffset() 如果包裝了數組,返回數組被用作緩沖的起始位置(可以將數組的一部分包裝成緩沖區)。
- boolean isDirect()
- boolean isReadOnly()
最最常用的緩沖區類ByteBuffer和CharBuffer已經在前一篇中寫過 : http://www.cnblogs.com/pixy/p/4779820.html
MappedByteBuffer
- boolean isLoaded()
- MappedByteBuffer load()
- MappedByteBuffer force()
文件加鎖機制
文件加鎖機制可以防止多個同時執行的程序修改同一個文件導致文件被破壞。文件加鎖也通過FileChannel類實現。
文件鎖是由整個Java虛擬機持有的,由同一個虛擬器啟動的不同Java進程不能同時獨占鎖定同一個文件。
文件加鎖機制依賴於底層文件系統,有些系統無法鎖定,有些系統鎖定后不能映射,有些系統關閉一個通道會是否對應文件上所有的鎖(避免在一個文件上使用多個通道),網絡文件系統盡量避免使用鎖。
FileChannel
- FileLock lock() 阻塞直到獲得鎖
- FileLock lock(long position, long size, boolean shared) 鎖定文件的一部分,阻塞直到獲得鎖(鎖定區域之后新增內容不會被鎖,除非size設為Long.MAX_VALUE), shared設為true時獲得共享稅(允許多個進程讀入,阻止任何進程獨占)。
- FileLock tryLock() 立刻返回,無法獲得鎖時返回null
- FileLock tryLock(long position, long size, boolean shared)
FileLock
- void close()
- boolean isShared() 返回是否為共享鎖
鏈接文件
- Path link = FileSystems.getDefault().getPath("realFile");
- Path target = FileSystems.getDefault().getPath("linkfile");
- //create hard link
- Files.createLink(link, target);
- //create symbolic link
- Files.createSymbolicLink(link, target);
- //create symbolic with file attribute
- PosixFileAttributes attrs = Files.readAttributes(target, PosixFileAttributes.class);
- FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(attrs.permissions());
- Files.createSymbolicLink(link, target, attr);
- boolean isSymbolciLink1 = Files.isSymbolicLink(link);
- boolean isSymbolciLink2 = (boolean) Files.getAttribute(link, "basic:isSymbolicLink");
- //get linked file
- Path linkedPath=Files.readSymbolicLink(link);