本篇教學文章示範如何使用Java不透過第三方套件來判斷檔案的副檔名,以及檔案類型,只需要Java 7的NIO就可以達成,再加上Java 8的Optional與Stream API來更優雅的處理。使用原生的API可以避免一些問題,例如執行時還需要第三方套件、第三方套件還包含太多用不上的API徒增檔案大小、第三方套件不更新可能有漏洞等麻煩。
取得副檔名
副檔名是檔案名稱中最後一個.後面的文字,通常是英文與數字,例如.jpg、.mp3、.txt等。雖然使用org.apache.commons.io.FilenameUtils可以找到副檔名,但我們目標是你複製以下程式碼就可以直接使用,不用再安裝第三方套件。
找出.的位置
這邊只透過Java的String內建的方法來尋找副檔名,效率也不錯。
// 副檔名預設為空字串
String extension = "";
// 我們要判斷的檔案名稱
String filename = "klab.tw.txt";
// 尋找最後一個.的位置,沒找到會回傳 -1
int idx = filename.lastIndexOf(".");
if(idx >= 0) {
// 使用substring取得副檔名,會得到 txt
extension = filename.substring(idx + 1);
}
使用Optional寫成完整版
Java 8開始增加一系列Stream API,我們重寫然後寫成一個Class,可以直接呼叫使用。
import java.util.Optional;
/**
* Create by https://klab.tw
*/
public class FileTools {
/**
* 從檔案名稱判斷副檔名。
* @param filename 檔案名稱
* @param orElse 找不到的時候返回的預設值,可為null
* @return 副檔名
*/
public static String getExtension(String filename, String orElse) {
return Optional.ofNullable(filename)
.filter(s -> s.contains("."))
.map(s -> s.substring(s.lastIndexOf(".") + 1))
.orElse(orElse);
}
}
使用方法如下。
FileTools.getExtension("klab.tw.txt", "");
// 返回:txt
取得檔案類型
在這邊我們使用Java 7開始內建的NIO API,使用Files直接透過副檔名判斷檔案類型。雖然Windows檔案總管預設會隱藏副檔名,但副檔名是人為決定的,而且很好修改,不能作為判斷檔案類型的唯一方式,但還是可以作為參考。
使用Path與Files
使用java.nio.file.Path建立Path物件,然後透過java.nio.file.Files內建的方法即可判斷。找不到的時候會回傳null,而且需要處理IOException。
String filename = "klab.tw.txt";
// Java 7~10使用 Paths.get(filename),Java 11之後可改寫為 Path.of(filename)
Path path = Paths.get(filename);
String mime = Files.probeContentType(path);
Paths.get 與 Path.of 的差別
這兩個方法功能完全一樣,都是把字串轉成Path物件,差別只在於放在哪個Class上。Paths.get()是Java 7與NIO一起推出的工具類別方法,當時Path本身是介面,沒辦法放static factory method。而Path.of()是Java 11之後加上去的,因為Java 8已經允許介面有static method,因此官方把工廠方法直接搬到Path介面本身。
從Java 11開始,Paths.get()內部其實只是呼叫Path.of(),兩個是等價的。新寫的程式建議使用Path.of(),跟List.of()、Set.of()、Map.of()這些Java 9之後的工廠方法風格一致,也少import一個Paths類別。但是如果還在維護Java 8的舊系統,就只能繼續使用Paths.get()。
| 方法 | 所在Class | 引入版本 |
|---|---|---|
| Paths.get() | java.nio.file.Paths | Java 7 |
| Path.of() | java.nio.file.Path | Java 11 |
跨平台的注意事項
另外要注意Files.probeContentType實際運作方式跟作業系統有關,Linux會讀取/etc/mime.types、macOS有自己的對應表,而Windows在某些JDK版本上對應較少,常會回傳null。如果需要跨平台一致的結果,建議自己維護副檔名對應表。
使用Optional寫成完整版
以下搭配Java 8的Stream API,寫可以直接呼叫使用、不用管IOException的完整版。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
/**
* Create by https://klab.tw
*/
public class FileTools {
/**
* 透過檔案副檔名判斷檔案類型。
* @param filename 檔案名稱
* @param orElse 找不到的時候返回的預設值,可為null
* @return 檔案類型
*/
public static String getContentType(String filename, String orElse) {
return Optional.ofNullable(filename)
.map(s -> Paths.get(s))
.map(p -> {
try {
return Files.probeContentType(p);
} catch (IOException e) {
return null;
}
})
.orElse(orElse);
}
}
使用方法如下。
FileTools.getContentType("klab.tw.txt", "");
// 返回:text/plain