Java IO

Posted by Adam on August 24, 2022
### byte[] to File ```java import org.apache.commons.io.IOUtils; import java.io.FileOutputStream; import java.io.IOException; public class WriteByteArrayToFileExample { public static void main(String[] args) { byte[] byteArray = {65, 66, 67, 68, 69}; // Sample byte array try { FileOutputStream fos = new FileOutputStream("output.txt"); IOUtils.write(byteArray, fos); fos.close(); System.out.println("Byte array has been written to file successfully."); } catch (IOException e) { System.err.println("Error writing byte array to file: " + e.getMessage()); } } } ``` 在上面的範例中,我們將一個包含 ASCII 值的 byte array 寫入到一個檔案中,並使用 Apache Commons IO 的 IOUtils 來執行實際的寫入動作。如果成功寫入檔案,將會列印出 "Byte array has been written to file successfully.",如果有任何錯誤發生,將會列印出錯誤訊息。 # Paths 在 Java NIO 裡,`Paths.get(...)` 只是把你傳進去的字串「包成」一個 `Path` 物件,它本身並不會去打開檔案、也不會檢查許可權──真正存取(讀/寫/刪除)還是要靠 `Files` 這一類 API,或者呼叫 `Path.toFile()` 之後用傳統的 `File` API。 因此: 1. 你可以用 `Paths.get(...)` 產生任何字串形式合法的路徑(絕對、相對、包含 `..`、Windows 的 UNC 路徑…) 2. 只有當你用 `Files.readAllLines(path)`、`Files.newBufferedReader(path)`、`Files.delete(path)` … 等方法去操作時,才會觸發: - OS 層級的「檔案是否存在」(不存在拋 `NoSuchFileException`) - OS 層級的「權限檢查」(沒讀取權限拋 `AccessDeniedException`) - 如果程式有啟用 Java SecurityManager,也可能被 `AccessControlException` 攔下 範例 1:任意路徑都可以包成 Path,但讀檔時才真正驗權限 ``` import java.nio.file.*; public class Demo { public static void main(String[] args) throws Exception { // 假設使用者輸入的路徑 String userInput = "/etc/passwd"; Path p = Paths.get(userInput); // 下面這行如果程式執行帳號沒權限,就會拋 AccessDeniedException // 或檔案不存在就拋 NoSuchFileException for (String line : Files.readAllLines(p)) { System.out.println(line); } } } ``` 範例 2:防止「路徑穿越」(Path Traversal) 如果你只想讓使用者存取某個基底資料夾(例如 `/home/app/data`)裡的檔案,就要在開檔前先把路徑正規化、並檢查不超出基底目錄: ``` import java.nio.file.*; public class SafeDemo { public static void main(String[] args) throws Exception { String baseDir = "/home/app/data"; String userInput = "../etc/passwd"; // 惡意輸入 Path basePath = Paths.get(baseDir).toRealPath(); // 正規化、展開符號連結 Path target = basePath.resolve(userInput).normalize(); if (!target.startsWith(basePath)) { throw new SecurityException("非法路徑存取:" + target); } // 通過檢查後再安心讀檔 Files.lines(target).forEach(System.out::println); } } ``` 重點整理 - Paths.get(...) 本身「不會」限制你傳多高多深、多上層或多不同磁碟分割區,它只負責把字串轉成 `Path`。 - 真正的檔案存在性、讀寫刪除權限檢查是由後面呼叫 `Files`、`FileChannel`… 等 I/O API 時,跟底層作業系統互動時才會發生。 - 若你的程式有「只能訪問應用程式資料目錄」之類的安全需求,務必要自行做路徑正規化(`toRealPath()` + `normalize()`)並檢查 `startsWith(basePath)`。