Files類提供了很多方法用于檢查在于你真正實際去操作一個文件或目錄。這些方法強烈推薦,也非常有用,也能避免很多異常的發生。例如,一個很好的習慣就是在你試著移動一個文件從一個地方到另一個地方的時候,先檢查文件是否存在。
檢查一個文件或目錄是否存在
在前面的例子中已經演示到,Path實例能夠有效的映射到一個文件或是目錄甚至這個文件或目錄物理上根本不存在。再是,Path的很多方法不會實際操作文件本身就能成功地應用。所以,事先判斷一個目錄或是文件存在就顯得非常重要。下面有兩個方法用來判斷文件是否存在。
exists()
:檢查一個文件是否存在notExists()
: 檢查一個文件是否不存在
這兩個方法都包含兩個參數,第一個參數是path實例,第二個參數符號連接的文件是否處理。exist()方法返回 true如果文件存在。下下面代碼:
Path path = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2009","AEGON.txt");…boolean path_exists = Files.exists(path, new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
注意:!Files.exists(…)
與Files.notExists(…)
方法不是等價的,notExists()不是
exists()
方法的補充。
檢查文件的可訪問性
一個很好的習慣就是在訪問一個文件前先檢查它的可訪問級別,可以使用isReadable()
, isWritable()
, isExecutable()這些方法。在傳遞一個路徑被確認后,如果文件可讀,可寫,可執行,那么虛擬機將有權限去訪問這個文件。
另外,可以使用isRegularFile()方法在判斷文件是不是一個正常的文件。正常的文件是指沒有特別的特性(例如,不是符號鏈接,不是目錄等),包含真實的數據(例如二進制文件)。
上代碼。
C:\rafaelnadal\tournaments\2009 directory (the file must exist) looks like the following:Path path = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2009","AEGON.txt");boolean is_readable = Files.isReadable(path);boolean is_writable = Files.isWritable(path);boolean is_executable = Files.isExecutable(path);boolean is_regular = Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS);if ((is_readable) && (is_writable) && (is_executable) && (is_regular)) {System.out.println("The checked file is accessible!");} else {System.out.println("The checked file is not accessible!");}
或者像下面這樣的代碼:
boolean is_accessible = Files.isRegularFile(path) & Files.isReadable(path) & Files.isExecutable(path) & Files.isWritable(path);if (is_accessible) {System.out.println("The checked file is accessible!");} else {System.out.println("The checked file is not accessible!");}
? 檢查兩個路徑是否指向同一相同文件
在以前的章節中,有可以看到如何去檢查一個符號鏈接和目標文件是否是同一個文件。還有一個常用的測試就是你你可以使用isSameFile()方法去檢查不同的Paths的表達是否指向同一個文件。例如,一個相對路徑和一個絕對路徑可能指向同一個文件。假設有一文件的路徑為
C:\rafaelnadal\tournaments\2009\
MutuaMadridOpen.txt,有下面三種Path的表達方式。
Path path_1 = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2009","MutuaMadridOpen.txt"); Path path_2 = FileSystems.getDefault().getPath("/rafaelnadal/tournaments/2009", "MutuaMadridOpen.txt"); Path path_3 = FileSystems.getDefault().getPath("/rafaelnadal/tournaments/dummy/../2009", "MutuaMadridOpen.txt");try {boolean is_same_file_12 = Files.isSameFile(path_1, path_2);boolean is_same_file_13 = Files.isSameFile(path_1, path_3);boolean is_same_file_23 = Files.isSameFile(path_2, path_3);System.out.println("is same file 1&2 ? " + is_same_file_12);System.out.println("is same file 1&3 ? " + is_same_file_13);System.out.println("is same file 2&3 ? " + is_same_file_23);} catch (IOException e) {System.err.println(e);}
輸出結果為:
is same file 1&2 ? true
is same file 1&3 ? true
is same file 2&3 ? true
檢查文件是否可見。
如果你需要找出一個文件是否可見,可以使用Files.isHidden()
方法,需要注意的是,“hidden”這個概念是依賴平臺的。
Path path = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2009", "MutuaMadridOpen.txt");…try {boolean is_hidden = Files.isHidden(path);System.out.println("Is hidden ? " + is_hidden);} catch (IOException e) {System.err.println(e);}
--------------------------------------------------------------------------------------------------------------------------------
創建和讀取目錄
當我們需要創建和讀取一個目錄時,NIO.2提供了一些類方法在Files類中。在這一節中,你將會發現如何列出文件系統的根,創建目錄以及臨時目錄,列出目錄里的內容,還可以對目錄進行過濾。
列出文件系統的根目錄
在Java 6 中,文件系統的根目錄被抽取出一個存放File對象的數組中,從Java 7 開始,NIO.2 會把根目錄作為Path對象的Iterable,通過getRootDirectories()
方法返回這個Iterable
接口。
Iterable<Path> dirs = FileSystems.getDefault().getRootDirectories();for (Path name : dirs) {System.out.println(name);}
可能的輸出結果是:
C:\
D:\
E:\
創建一個新的目錄
直接上代碼。
Path newdir = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2010/");…try {Files.createDirectory(newdir);} catch (IOException e) {System.err.println(e);}
當然,也可以在創建目錄時設置權限信息:
Path newdir = FileSystems.getDefault().getPath("/home/rafaelnadal/tournaments/2010/");…Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);try {Files.createDirectory(newdir, attr);} catch (IOException e) {System.err.println(e);}
注意,如果要創建的目錄存在,則?createDirectory()方法會拋出異常。
有的時候,我們需要多層的目錄,例如\statistics\win\prizes
,當然你可以使用createDirectory()
方法,其實還可以優雅地使用Files.createDirectories()方法,
Path newdir= FileSystems.getDefault().getPath("C:/rafaelnadal/", "statistics/win/prizes");…try {Files.createDirectories(newdir);} catch (IOException e) {System.err.println(e);}
列出目錄里的內容
Path path = Paths.get("C:/rafaelnadal/tournaments/2009");//no filter appliedSystem.out.println("\nNo filter applied:");try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {for (Path file : ds) {System.out.println(file.getFileName());}}catch(IOException e) {System.err.println(e);}
使用正則表達式列出目錄里的內容
Path path = Paths.get("C:/rafaelnadal/tournaments/2009");…//glob pattern appliedSystem.out.println("\nGlob pattern applied:");try (DirectoryStream<Path> ds = Files.newDirectoryStream(path, "*.{png,jpg,bmp}")) {for (Path file : ds) {System.out.println(file.getFileName());}} catch (IOException e) {System.err.println(e);}
用戶自定義過濾器列出目錄里的內容
如果正則表達式不能滿足你的需求,你也可以自定義自己的過濾器,這個任務很簡單,只需要繼承DirectoryStream.Filter<T>接口即可。此接口有accept()方法,一個Path是接受還是拒絕完全基于你的實現。下面的例子,只列出給定目錄下的所有目錄。
Path path = Paths.get("C:/rafaelnadal/tournaments/2009");…//user-defined filter - only directories are acceptedDirectoryStream.Filter<Path> dir_filter = new DirectoryStream.Filter<Path>() {public boolean accept(Path path) throws IOException {return (Files.isDirectory(path, NOFOLLOW_LINKS));}};
System.out.println("\nUser defined filter applied:");try (DirectoryStream<Path> ds = Files.newDirectoryStream(path, dir_filter)) {for (Path file : ds) {System.out.println(file.getFileName());}} catch (IOException e) {System.err.println(e);}
還有如下自定義的過濾器。
1.列出文件或目錄大小超過200kb的列表。
public boolean accept(Path path) throws IOException {return (Files.size(path) > 204800L);}};
2.列出只有今天修改的文件的列表。
DirectoryStream.Filter<Path> time_filter = new DirectoryStream.Filter<Path>() {public boolean accept(Path path) throws IOException {long currentTime = FileTime.fromMillis(System.currentTimeMillis()).to(TimeUnit.DAYS);long modifiedTime = ((FileTime) Files.getAttribute(path, "basic:lastModifiedTime", NOFOLLOW_LINKS)).to(TimeUnit.DAYS);if (currentTime == modifiedTime) {return true;}return false;}};
3.只列出隱藏的文件或目錄
DirectoryStream.Filter<Path> hidden_filter = new DirectoryStream.Filter<Path>() {public boolean accept(Path path) throws IOException {return (Files.isHidden(path));}};
-----------------------------------------------------------------------------------------------------------------------------
? 創建,讀取和寫文件
一個Stream表示輸入源或是輸出目的地。stream支持不同種類的數據,例如字符串,字節,基本數據類型,本地化字符還有對象。在一個無緩沖的流,每個讀或寫請求由底層操作系統直接處理,而在有緩存的流中,數據從內存中稱之為緩沖區的地方讀取,只有當緩沖區為空的時候本地輸入API會被調用。類似的,緩存的輸出流把數據寫入到緩沖區中,只有緩沖區寫滿以后本地輸出API才會被調用。當緩沖區輸出完沒有等待填滿時,外我們說這個緩沖區是清空的。
使用標準的打開選項
從NIO.2 開始,文件的創建,讀取和寫入操作都支持一個可選參數——OpenOption,它用來配置外面如何打開或是創建一個文件。實際上OpenOption是java.nio.file
包中一個接口,它有兩個實現類:LinkOption
和 StandardOpenOption
。下面就是選項枚舉。
READ | 以讀取方式打開文件 |
WRITE | 已寫入方式打開文件 |
CREATE | 如果文件不存在,創建 |
CREATE_NEW | 如果文件不存在,創建;若存在,異常。 |
APPEND | 在文件的尾部追加 |
DELETE_ON_CLOSE | 當流關閉的時候刪除文件 |
TRUNCATE_EXISTING | 把文件設置為0字節 |
SPARSE | 文件不夠時創建新的文件 |
SYNC | 同步文件的內容和元數據信息隨著底層存儲設備 |
DSYNC | 同步文件的內容隨著底層存儲設備 |
?
?
?
?
?
?
?
?
?
?
? 創建一個文件
創建文件和創建目錄類似,可以使用Files.createFile()
方法,也可以在創建的時候添加權限信息。
Path newfile = FileSystems.getDefault().getPath("C:/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");…try {Files.createFile(newfile);} catch (IOException e) {System.err.println(e);}
創建帶有權限的文件.
Path newfile = FileSystems.getDefault().getPath("/home/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);try {Files.createFile(newfile, attr);} catch (IOException e) {System.err.println(e);}
寫入一個小文件
NIO.2提供了非常優雅的方式去寫入小的二進制或是文本文件。使用Files.write()方法來創建。
使用write()方法寫入bytes
Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");…byte[] ball_bytes = new byte[]{(byte)0x89,(byte)0x50,(byte)0x4e,(byte)0x47,(byte)0x0d,(byte)0x0a,(byte)0x1a,(byte)0x0a,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0d,(byte)0x49,(byte)0x48,(byte)0x44,(byte)0x52,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,(byte)0x08,(byte)0x02,(byte)0x00, …(byte)0x49,(byte)0x45,(byte)0x4e,(byte)0x44,(byte)0xae,(byte)0x42,(byte)0x60,(byte)0x82 };try {Files.write(ball_path, ball_bytes);} catch (IOException e) {System.err.println(e);}
按行寫入文件
Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");…Charset charset = Charset.forName("UTF-8");ArrayList<String> lines = new ArrayList<>();lines.add("\n");lines.add("Rome Masters - 5 titles in 6 years");lines.add("Monte Carlo Masters - 7 consecutive titles (2005-2011)");lines.add("Australian Open - Winner 2009");lines.add("Roland Garros - Winner 2005-2008, 2010, 2011");lines.add("Wimbledon - Winner 2008, 2010");lines.add("US Open - Winner 2010");try {Files.write(rf_wiki_path, lines, charset, StandardOpenOption.APPEND);} catch (IOException e) {System.err.println(e);}
? 讀取一個小文件
NIO.2 提供了兩個方法Files.readAllBytes()
?和 Files.readAllLines()方法用來讀取小的字節或是文本文件。很簡單,直接看代碼。
Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");…try {byte[] ballArray = Files.readAllBytes(ball_path); } catch (IOException e) {System.out.println(e);}
Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");…Charset charset = Charset.forName("ISO-8859-1");try {List<String> lines = Files.readAllLines(wiki_path, charset);for (String line : lines) {System.out.println(line);}} catch (IOException e) {System.out.println(e);}
根據官方文檔,該方法識別以下行認為是結束符:
\u000D
followed by\u000A
: 回車接著換行\u000A
: 換行\u000D
:回車
使用緩存流進行工作
在多數操作系統中,系統調用文件的讀寫是一件非常昂貴的操作。在緩沖的方法區和操作系統之間提供了一塊內存區域很好的解決了這個問題。
在調用本地API之前,這些方法在操作系統和應用程序之間的緩存中獲取或設置數據。這樣大大地提高了效率因為它減少了調用系統的次數。只有在緩沖區為空或是滿的時候,才會訪問硬盤,根據讀取或寫入操作。NIO.2提供了兩個通過緩存讀取和寫入文件的方法:Files.newBufferedReader()
和Files.newBufferedWriter(),相應的,這兩個方法會得到Path實例并返回
BufferedReader
或 BufferedWriter
實例。
使用?newBufferedWriter()方法
? 不多說,上代碼。
Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");…Charset charset = Charset.forName("UTF-8");String text = "\nVamos Rafa!";try (BufferedWriter writer = Files.newBufferedWriter(wiki_path, charset, StandardOpenOption.APPEND)) {writer.write(text);} catch (IOException e) {System.err.println(e);}
使用newBufferedReader()方法
Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");…Charset charset = Charset.forName("UTF-8");try (BufferedReader reader = Files.newBufferedReader(wiki_path, charset)) {String line = null;while ((line = reader.readLine()) != null) {System.out.println(line);}} catch (IOException e) {System.err.println(e);}
? 使用無緩存的流工作
可以使用NIO.2直接獲取無緩存的流也可以使用java.io的API中的包裝類轉換成緩存流。使用無緩存的流的方法有Files.newInputStream()(從一個文件中讀取到輸入流中),
Files.newOutputStream()
方法(從輸出流中寫入到文件中)。
使用newOutputStream() 方法
這個方法獲取指向文件的路徑和說明文件時如何打開的,它會返回一個線程安全的無緩存的劉對象,用來寫入字節到文件中。
上菜。
//C:\rafaelnadal\equipment\racquet.txt (the file doesn’t initially exist, but it will be automatically created because no options are specified):Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");String racquet = "Racquet: Babolat AeroPro Drive GT";byte data[] = racquet.getBytes();try (OutputStream outputStream = Files.newOutputStream(rn_racquet)) {outputStream.write(data);} catch (IOException e) {System.err.println(e);}
此外,如果你有更好的主意使用緩存的流代替上面的代碼,推薦使用基于java.io的API的轉換,正如下面的代碼,
Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");String string = "\nString: Babolat RPM Blast 16";try (OutputStream outputStream = Files.newOutputStream(rn_racquet, StandardOpenOption.APPEND);BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {writer.write(string);} catch (IOException e) {System.err.println(e);}
使用?newInputStream()方法
//The following code snippet reads the content of the file racquet.txt (the file must exist): Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");…int n; try (InputStream in = Files.newInputStream(rn_racquet)) {while ((n = in.read()) != -1) {System.out.print((char)n); }} catch (IOException e) {System.err.println(e);}
從此而外,你也可以把一個無緩存的流轉換成緩存流,下面的代碼跟上面的代碼實現了相同的功能,但是它更加高效。
Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");…try (InputStream in = Files.newInputStream(rn_racquet);BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {String line = null;while ((line = reader.readLine()) != null) {System.out.println(line);}} catch (IOException e) {System.err.println(e);}
------------------------------------------------------------------------------------------------------------------------
? 創建臨時目錄和文件
一個臨時目錄用來存放臨時文件。臨時目錄的存放位置依賴于操作系統。在Windows下,臨時目錄可以通過“TEMP”環境變量來設置,通常的位置是:C:\Temp
, %Windows%\Temp,或者在每個用戶的:
Local Settings\Temp。而Unix/Linux的臨時目錄為:
/tmp
和/var/tmp。
創建一個臨時目錄
在NIO.2中通過createTempDirectory()方法用來創建一個臨時目錄,創建默認的操作系統的臨時目錄可以調用createTempDirectory()兩個參數的方法:前一個用來設置目錄的名字的前綴(可以為null),后一個可選參數用來設置文件屬性。
String tmp_dir_prefix = "nio_";try {//passing null prefixPath tmp_1 = Files.createTempDirectory(null);System.out.println("TMP: " + tmp_1.toString());//set a prefixPath tmp_2 = Files.createTempDirectory(tmp_dir_prefix);System.out.println("TMP: " + tmp_2.toString());} catch (IOException e) {System.err.println(e);}
則輸出結果為:
TMP: C:\Users\Leo\AppData\Local\Temp\3238630399269555448
TMP: C:\Users\Leo\AppData\Local\Temp\nio_1097550355199661257
如果你不知道系統的默認臨時目錄的路徑,也可以使用下面的代碼:
//output: C:\Users\Leo\AppData\Local\Temp\String default_tmp = System.getProperty("java.io.tmpdir");System.out.println(default_tmp);
更進一步,你也可以通過createTempDirectory()方法自定義臨時目錄的路徑,
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp/");String tmp_dir_prefix = "rafa_";try {if (Files.notExists(basedir)) {Path dir = Files.createDirectories(basedir);// create a tmp directory in the base dirPath tmp = Files.createTempDirectory(dir, tmp_dir_prefix);System.out.println("TMP: " + tmp.toString());}} catch (IOException e) {System.err.println(e);}
輸出結果為:
TMP: C:\rafaelnadal\tmp\rafa_1753327229539718259
使用Shutdown-Hook刪除臨時文件
大部分操作系統都會自動地刪除臨時目錄(如果不能,你可以使用多種清理軟件),但是,有時候你需要程序級別的控制文件的刪除過程。createTempDirectory()方法只是完成了一半的工作,因為刪除工作由你負責。為了這一原因你可以shutdown-hook機制,這個機制用來執行任何資源的清理或者保持在JVM關閉之前生效。這個鉤子可以是Java線程的實現。Thread的run()方法可以在JVM關閉時執行操作。
Figure 4-1. The simple flow design of a shutdown-hook
Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {System.out.println("Shutdown-hook activated ...");//… here, cleanup/save resources System.out.println("Shutdown-hook successfully executed ...");}});
shutdown-hook機制是一個很好的方法用來解決JVM關閉時刪除臨時目錄的問題。同時你也知道,如果目錄不為空的話是無法刪除的。所以你需要循環每一層目錄刪除每一個文件直至每個目錄為空再刪除目錄本身。
使用deleteOnExit() 方法刪除臨時目錄
另一種刪除臨時目錄的解決方法是調用deleteOnExit()方法,這個方法在java.io.Fi類中。它將在JVM關閉時刪除傳遞的文件或目錄參數。因為這個方法需要被每個目錄和文件調用,所以它最不吸引人的地方就是需要為每個臨時實體消耗內存。
注意,如果你的系統需要長時間運行或者在很短的時間內需要創建很多文件和目錄,則使用deleteOnExit()是個很壞的主意。它需要占用大量內存即使JVM退出后還沒有釋放。
下面演示deleteOnExit()方法的使用。
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp/");String tmp_dir_prefix = "rafa_";try {//create a tmp directory in the base dirPath tmp_dir = Files.createTempDirectory(basedir, tmp_dir_prefix);File asFile = tmp_dir.toFile();asFile.deleteOnExit();//simulate some I/O operations over the temporary file by sleeping 10 seconds//when the time expires, the temporary file is deleted //EACH CREATED TEMPORARY ENTRY SHOULD BE REGISTERED FOR DELETE ON EXITThread.sleep(10000);//operations done } catch (IOException | InterruptedException e) {System.err.println(e);}
創建臨時文件
不羅嗦,一些概念跟創建臨時目錄一樣,直接上代碼。
String tmp_file_prefix = "rafa_";String tmp_file_sufix=".txt";try {//passing null prefix/suffixPath tmp_1 = Files.createTempFile(null,null);System.out.println("TMP: " + tmp_1.toString());//set a prefix and a suffixPath tmp_2 = Files.createTempFile(tmp_file_prefix, tmp_file_sufix);System.out.println("TMP: " + tmp_2.toString());} catch (IOException e) {System.err.println(e);}
同樣,你也可以自定義臨時文件的目錄。
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp");String tmp_file_prefix = "rafa_";String tmp_file_sufix=".txt";try {Path tmp_3 = Files.createTempFile(basedir, tmp_file_prefix, tmp_file_sufix);System.out.println("TMP: " + tmp_3.toString());} catch (IOException e) {System.err.println(e);}
使用?Shutdown-Hook機制刪除臨時文件
下面代碼在C:\rafaelnadal\tmp下創建臨時文件,等待10秒鐘后,當JVM退出后刪除臨時文件。
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp");String tmp_file_prefix = "rafa_";String tmp_file_sufix = ".txt";try {final Path tmp_file = Files.createTempFile(basedir, tmp_file_prefix, tmp_file_sufix);Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {System.out.println("Deleting the temporary file ...");try {Files.delete(tmp_file);} catch (IOException e) {System.err.println(e);}System.out.println("Shutdown hook completed...");}});//simulate some I/O operations over the temporary file by sleeping 10 seconds//when the time expires, the temporary file is deleted Thread.sleep(10000);//operations done } catch (IOException | InterruptedException e) {System.err.println(e);}
使用?deleteOnExit() 方法刪除臨時文件
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp");String tmp_file_prefix = "rafa_";String tmp_file_sufix = ".txt";try {final Path tmp_file = Files.createTempFile(basedir, tmp_file_prefix, tmp_file_sufix);File asFile = tmp_file.toFile();asFile.deleteOnExit();//simulate some I/O operations over the temporary file by sleeping 10 seconds//when the time expires, the temporary file is deletedThread.sleep(10000);//operations done } catch (IOException | InterruptedException e) {System.err.println(e);}
使用DELETE_ON_CLOSE枚舉參數刪除臨時文件
另一種比較獨特的刪除臨時文件的方式是使用DELETE_ON_CLOSE選項。它會在流關閉的時候刪除文件。
Path basedir = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp");String tmp_file_prefix = "rafa_";String tmp_file_sufix = ".txt";Path tmp_file = null;try {tmp_file = Files.createTempFile(basedir, tmp_file_prefix, tmp_file_sufix);} catch (IOException e) {System.err.println(e);}try (OutputStream outputStream = Files.newOutputStream(tmp_file, StandardOpenOption.DELETE_ON_CLOSE);BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {//simulate some I/O operations over the temporary file by sleeping 10 seconds//when the time expires, the temporary file is deleted Thread.sleep(10000);//operations done} catch (IOException | InterruptedException e) {System.err.println(e);}
除此而外,你甚至不用調用createTempFile()方法,當你使用
CREATE選項與
DELETE_ON_CLOSE組合使用時:
String tmp_file_prefix = "rafa_";String tmp_file_sufix = ".txt";Path tmp_file = null;tmp_file = FileSystems.getDefault().getPath("C:/rafaelnadal/tmp", tmp_file_prefix + "temporary" + tmp_file_sufix);try (OutputStream outputStream = Files.newOutputStream(tmp_file, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {//simulate some I/O operations over the temporary file by sleeping 10 seconds//when the time expires, the temporary file is deleted Thread.sleep(10000);//operations done} catch (IOException | InterruptedException e) {System.err.println(e);}
目錄或文件的刪除,復制和移動
刪除目錄或文件
NIO.2提供了兩個用來刪除目錄或文件的方法:Files.delete()
和Files.deleteIfExits()。這兩個都接受單一的參數指定刪除的路徑。但是Files.delete()無返回值,
Files.deleteIfExits()返回boolean值根據文件是否刪除成功。Files.delete()試圖刪除路徑下的文件或目錄,如果刪除失敗,則會拋出以下異常。
NoSuchFileException
(i路徑不存在), DirectoryNotEmptyException
(i目錄不為空), IOException
(輸入輸出錯誤發生), or SecurityException
(無刪除權限)。
Path path = FileSystems.getDefault().getPath("C:/rafaelnadal/photos", "rafa_1.jpg");//delete the filetry {Files.delete(path);} catch (NoSuchFileException | DirectoryNotEmptyException | IOException | SecurityException e) {System.err.println(e);}
就像名字建議的一樣,Files.deleteIfExists()方法只有在文件存在的時候刪除,這就意味著如果文件不存在(代替了拋出
NoSuchFileException異常
)不能刪除的情況下返回boolean值false。這種應用在多線程刪除文件的時候非常有用。你不想第一個線程就拋出異常。
try {boolean success = Files.deleteIfExists(path);System.out.println("Delete status: " + success);} catch (DirectoryNotEmptyException | IOException | SecurityException e) {System.err.println(e);}
復制目錄或文件
NIO.2中提供了Files.copy()方法來復制目錄和文件。它提供了
StandardCopyOption
和 LinkOption 枚舉下的很多選項作為參數:
REPLACE_EXISTING
: 如果目標文件存在,則替換;當拷貝符號鏈接文件時,真實文件不拷貝,值拷貝符號鏈接文件。COPY_ATTRIBUTES
: 拷貝文件并關聯的屬性。NOFOLLOW_LINKS
: 不包含符號鏈接的文件或目錄.
這些枚舉類型靜態導入到程序中,
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
在兩個路徑間復制
Path copy_from = Paths.get("C:/rafaelnadal/grandslam/AustralianOpen", "draw_template.txt");Path copy_to= Paths.get("C:/rafaelnadal/grandslam/USOpen",copy_from.getFileName().toString());try {Files.copy(copy_from, copy_to, REPLACE_EXISTING, COPY_ATTRIBUTES, NOFOLLOW_LINKS);} catch (IOException e) {System.err.println(e);}
? 從一個輸入流中拷貝到文件
Path copy_from = Paths.get("C:/rafaelnadal/grandslam/AustralianOpen", "draw_template.txt");Path copy_to = Paths.get("C:/rafaelnadal/grandslam/Wimbledon", "draw_template.txt");try (InputStream is = new FileInputStream(copy_from.toFile())) {Files.copy(is, copy_to, REPLACE_EXISTING);} catch (IOException e) {System.err.println(e);}
文件輸入流可能來自不同的方式。例如下面的代碼中文件來自于互聯網的URL。
Path copy_to = Paths.get("C:/rafaelnadal/photos/rafa_winner_2.jpg");URI u = URI.create("https://lh6.googleusercontent.com/--udGIidomAM/Tl8KTbYd34I/AAAAAAAAAZw/j2nH24PaZyM/s800/rafa_winner.jpg");try (InputStream in = u.toURL().openStream()) {Files.copy(in, copy_to);} catch (IOException e) {System.err.println(e);}
從一個文件拷貝到輸出流
Path copy_from = Paths.get("C:/rafaelnadal/grandslam/AustralianOpen", "draw_template.txt");Path copy_to = Paths.get("C:/rafaelnadal/grandslam/RolandGarros", "draw_template.txt");try (OutputStream os = new FileOutputStream(copy_to.toFile())) {Files.copy(copy_from, os);} catch (IOException e) {System.err.println(e);}
移動文件和目錄
在這部分,你將會學習到 如何使用Files.move()方法移動目錄和文件。這個方法有一可選參數用來指定一枚舉類型:
REPLACE_EXISTING
:若目標文件存在,則替換.ATOMIC_MOVE
: 執行的文件將被作為一個原子操作,保證任何過程監控文件的目錄將會訪問一個完整的文件。
默認情況下(沒有明確指定選項) move()嘗試移動文件到目標文件,如果目標文件存在,則會失敗。除非源文件與目標文件是同一文件(isSameFile()
返回 true),這種情況方法不會生效。
Path movefrom = FileSystems.getDefault().getPath("C:/rafaelnadal/rafa_2.jpg");Path moveto = FileSystems.getDefault().getPath("C:/rafaelnadal/photos/rafa_2.jpg");try {Files.move(movefrom, moveto, StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {System.err.println(e);}
如果你不想hard-code移動文件的名字,可以使用Path.resolve()
方法,通過這種方法,你可以直接從需要移動的路徑中抽取文件名字給移動文件的名字。
Path movefrom = FileSystems.getDefault().getPath("C:/rafaelnadal/rafa_2.jpg");Path moveto_dir = FileSystems.getDefault().getPath("C:/rafaelnadal/photos");System.out.println(moveto_dir.resolve(movefrom.getFileName())); // C:\rafaelnadal\photos\rafa_2.jpgtry {Files.move(movefrom, moveto_dir.resolve(movefrom.getFileName()), StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {System.err.println(e);}
重命名文件
這開起來有些詭異,可以通過Files.move()
和Path.resolveSibling() 重命名一個文件。
Path movefrom = FileSystems.getDefault().getPath("C:/rafaelnadal/photos/rafa_2.jpg");try {Files.move(movefrom, movefrom.resolveSibling("rafa_2_renamed.jpg"), StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {System.err.println(e);}
完。
?
?
?
?