Pogovor o klasama Paths i Files u Java NIO-u
Klase Paths i Files uvedene su u Javi 7, kao dopuna i unapređenje klase java.io.File.
Klasa Paths
Klasa Paths uglavnom služi za rad sa putanjama fajlova i direktorijuma. Pruža nekoliko statičkih metoda za kreiranje instanci java.nio.file.Path, koje predstavljaju putanje u fajl sistemu.
Evo jednog primera za Paths.
// Kreiranje Path instance koja predstavlja fajl u trenutnom direktorijumu
Path path = Paths.get("example.txt");
// Kreiranje apsolutne putanje
Path absolutePath = Paths.get("/home/user/example.txt");Interfejs java.nio.file.Path u okviru Java NIO.2 predstavlja putanju u fajl sistemu. On nudi niz metoda za rad sa putanjama i za upit nad njima.

Evo primera u kom se Paths i Path koriste zajedno:
Path path = Paths.get("docs/prateci-tutorijal.md");
// Dohvatanje imena fajla
System.out.println("File name: " + path.getFileName());
// Dohvatanje roditeljskog direktorijuma
System.out.println("Parent: " + path.getParent());
// Dohvatanje korenog direktorijuma
System.out.println("Root: " + path.getRoot());
// Spajanje putanje sa drugom putanjom
Path newPath = path.resolve("config/app.properties");
System.out.println("Resolved path: " + newPath);
// Normalizacija putanje
Path normalizedPath = newPath.normalize();
System.out.println("Normalized path: " + normalizedPath);
// Pretvaranje relativne putanje u apsolutnu
Path absolutePath = path.toAbsolutePath();
System.out.println("Absolute path: " + absolutePath);
// Računanje relativne putanje između dve putanje
Path basePath = Paths.get("/docs/");
Path targetPath = Paths.get("/docs/imgs/itwanger");
Path relativePath = basePath.relativize(targetPath);
System.out.println("Relative path: " + relativePath);Klasa Files
Klasa java.nio.file.Files pruža veliki broj statičkih metoda za rad sa fajlovima i direktorijumima u fajl sistemu. Te metode obuhvataju kreiranje, brisanje, kopiranje i pomeranje fajlova, kao i čitanje i postavljanje atributa fajlova.
Evo primera u kom se Files i Paths koriste zajedno:
// Kreiranje Path instance
Path path = Paths.get("logs/javabetter/itwanger4.txt");
// Kreiranje novog fajla
Files.createFile(path);
// Provera da li fajl postoji
boolean exists = Files.exists(path);
System.out.println("File exists: " + exists);
// Brisanje fajla
Files.delete(path);U nastavku su navedene neke od najčešće korišćenih metoda, sa primerima:
exists(Path path, LinkOption... options): proverava da li fajl ili direktorijum postoji.
Path path = Paths.get("file.txt");
boolean exists = Files.exists(path);
System.out.println("File exists: " + exists);LinkOption je enumeracija koja definiše opcije za rad sa fajl-sistemskim linkovima. Nalazi se u paketu java.nio.file. LinkOption se uglavnom koristi u metodama koje rade sa putanjama fajlova ili direktorijuma, kako bi se kontrolisalo način na koji te metode tretiraju simboličke linkove. Simbolički link je posebna vrsta fajla koja je česta na Unix i Unix-sličnim sistemima (poput Linuxa i macOS-a). Na Windowsu se sličan koncept naziva prečicom.
createFile(Path path, FileAttribute<?>... attrs): kreira novi, prazan fajl.
Path newPath = Paths.get("newFile.txt");
Files.createFile(newPath);FileAttribute je generički interfejs namenjen obradi različitih tipova atributa. Pri korišćenju FileAttribute potrebno je obezbediti njegovu konkretnu implementaciju. Klasa PosixFileAttributes iz paketa java.nio.file.attribute pruža implementaciju POSIX (Portable Operating System Interface — standard koji definiše mnoge operacije vezane za fajl sistem, uključujući kreiranje, brisanje, čitanje i izmenu fajlova i direktorijuma) atributa fajla.
Path path = Paths.get("fileWithPermissions.txt");
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rw-r-----");
FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions);
Files.createFile(path, fileAttribute);Interfejs PosixFileAttributes pruža metode za dohvatanje POSIX atributa fajla, poput vlasnika fajla, grupe kojoj fajl pripada i prava pristupa fajlu. Gornji primer kreira fajl sa atributima za čitanje i upisivanje.
createDirectory(Path dir, FileAttribute<?>... attrs): kreira novi direktorijum.
Path newDir = Paths.get("newDirectory");
Files.createDirectory(newDir);delete(Path path): briše fajl ili direktorijum.
Path pathToDelete = Paths.get("fileToDelete.txt");
Files.delete(pathToDelete);copy(Path source, Path target, CopyOption... options): kopira fajl ili direktorijum.
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFile.txt");
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);U Java NIO-u postoje dve enumeracije koje implementiraju CopyOption interfejs: StandardCopyOption i LinkOption.
Enumeracija StandardCopyOption pruža sledeće dve opcije:
- REPLACE_EXISTING: ako ciljni fajl već postoji, ova opcija će naterati metodu
Files.copy()da zameni ciljni fajl. Ako se ova opcija ne navede, metodaFiles.copy()će, u slučaju da ciljni fajl već postoji, baciti FileAlreadyExistsException. - COPY_ATTRIBUTES: ova opcija znači da se pri kopiranju fajla, koliko god je moguće, kopiraju i njegovi atributi (npr. vremenske oznake fajla, prava pristupa itd.). Ako se opcija ne navede, ciljni fajl će imati podrazumevane atribute.
move(Path source, Path target, CopyOption... options): pomera ili preimenuje fajl ili direktorijum.
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFile.txt");
Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);readAllLines(Path path, Charset cs): čita sve linije fajla u listu stringova.
Path path = Paths.get("file.txt");
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
lines.forEach(System.out::println);write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options): upisuje listu stringova u fajl.
Path path = Paths.get("file.txt");
List<String> lines = Arrays.asList("Chenmo Wang Er 1", "Chenmo Wang Er 2", "Chenmo Wang Er 3");
Files.write(path, lines, StandardCharsets.UTF_8);OpenOption je interfejs u Java NIO-u koji služi za konfigurisanje operacija nad fajlovima. Pruža opcije za prilagođavanje ponašanja pri korišćenju metoda Files.newByteChannel(), Files.newInputStream(), Files.newOutputStream(), AsynchronousFileChannel.open() i FileChannel.open().
U Java NIO-u postoje dve enumeracije koje implementiraju OpenOption interfejs: StandardOpenOption i LinkOption.
Enumeracija StandardOpenOption pruža sledeće opcije:
- READ: otvara fajl u režimu čitanja.
- WRITE: otvara fajl u režimu pisanja.
- APPEND: otvara fajl u režimu dodavanja (append).
- TRUNCATE_EXISTING: pri otvaranju fajla skraćuje njegov sadržaj tako da mu dužina bude 0. Odnosi se samo na WRITE ili APPEND režim.
- CREATE: ako fajl ne postoji, kreira ga; ako fajl već postoji, otvara ga.
- CREATE_NEW: ako fajl ne postoji, kreira ga; ako fajl već postoji, baca FileAlreadyExistsException.
- DELETE_ON_CLOSE: briše fajl pri zatvaranju kanala.
- SPARSE: sugeriše fajl sistemu da kreira tzv. retki (sparse) fajl.
- SYNC: zahteva da se svako ažuriranje sadržaja fajla ili metapodataka sinhronizuje.
- DSYNC: zahteva da se svako ažuriranje sadržaja fajla sinhronizuje.
newBufferedReader(Path path, Charset cs) i newBufferedWriter(Path path, Charset cs, OpenOption... options): kreira BufferedReader i BufferedWriter objekte za čitanje odnosno pisanje fajla.
Path path = Paths.get("file.txt");
// Čitanje fajla
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// Pisanje fajla
Path outputPath = Paths.get("outputFile.txt");
try (BufferedWriter writer = Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8)) {
writer.write("Chenmo Wang Er");
}Statička metoda Files.walkFileTree()
Ova metoda rekurzivno obilazi sve fajlove i direktorijume u strukturi direktorijuma i omogućava vam da nad njima izvršavate proizvoljne operacije. Pri korišćenju metode walkFileTree potrebno je obezbediti početnu putanju (početni direktorijum) i objekat koji implementira FileVisitor interfejs. FileVisitor interfejs sadrži četiri metode koje se pozivaju u različitim fazama obilaska:
- preVisitDirectory: poziva se pre obilaska direktorijuma.
- postVisitDirectory: poziva se nakon obilaska direktorijuma.
- visitFile: poziva se prilikom obilaska fajla.
- visitFileFailed: poziva se kada obilazak fajla ne uspe.
Pogledajmo sledeći primer:
public class WalkFileTreeExample {
public static void main(String[] args) {
Path startingDir = Paths.get("docs");
MyFileVisitor fileVisitor = new MyFileVisitor();
try {
Files.walkFileTree(startingDir, fileVisitor);
} catch (IOException e) {
e.printStackTrace();
}
}
private static class MyFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("Priprema za posetu direktorijumu: " + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("Poseta direktorijumu (u toku): " + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("Poseta fajlu: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.err.println("Poseta fajlu nije uspela: " + file);
return FileVisitResult.CONTINUE;
}
}
}Rezultat izvršavanja je sledeći:
Priprema za posetu direktorijumu: docs
Poseta fajlu: docs/instalacija-okruzenja.md
Priprema za posetu direktorijumu: docs/imgs
Poseta fajlu: docs/imgs/init_03.jpg
Priprema za posetu direktorijumu: docs/imgs/itwanger
Poseta fajlu: docs/imgs/itwanger/tongzhishu.jpeg
Poseta fajlu: docs/imgs/itwanger/tongzhishu1.jpeg
Poseta fajlu: docs/imgs/itwanger/tongzhishu1.pdf
Poseta direktorijumu (u toku): docs/imgs/itwanger
Poseta fajlu: docs/imgs/init_02.jpg
Poseta fajlu: docs/imgs/init_00.jpg
Poseta fajlu: docs/imgs/init_01.jpg
Poseta fajlu: docs/imgs/init_04.jpg
Poseta direktorijumu (u toku): docs/imgs
Poseta fajlu: docs/tutorijal-pokretanja-servera.md
Poseta fajlu: docs/prateci-tutorijal.md
Poseta fajlu: docs/dogovori.md
Poseta fajlu: docs/tutorijal-podesavanja-lokalnog-dev-okruzenja.md
Poseta fajlu: docs/opis-strukture-frontend-projekta.md
Poseta direktorijumu (u toku): docsU ovom primeru kreirali smo sopstvenu klasu MyFileVisitor koja implementira FileVisitor i proširuje klasu SimpleFileVisitor. SimpleFileVisitor je jedna implementacija FileVisitor interfejsa koja pruža podrazumevano ponašanje. Možemo nadjačati metode iz SimpleFileVisitor-a kako bismo implementirali sopstvenu logiku. U ovom primeru samo štampamo fajlove i direktorijume koje obilazimo. Zatim koristimo metodu Files.walkFileTree za obilazak stabla fajlova. Ta metoda obilazi celu strukturu direktorijuma i poziva odgovarajuće metode iz klase MyFileVisitor.
Pri tome, enumeracija FileVisitResult sadrži sledeće četiri opcije:
- CONTINUE: nastavi.
- TERMINATE: prekini.
- SKIP_SIBLINGS: preskoči braću (sibling čvorove), a zatim nastavi.
- SKIP_SUBTREE: preskoči podstablo (ne posećuje unose ovog direktorijuma), a zatim nastavi; ima smisla samo kada ga vrati metoda preVisitDirectory — inače se ponaša isto kao CONTINUE.
Pretraga fajlova
Metoda walkFileTree() se može koristiti i za pretragu fajlova. Sledeći primer proširuje SimpleFileVisitor kako bi pronašao fajl pod imenom itwanger.txt:
public class FindFileWithWalkFileTree {
public static void main(String[] args) {
Path startingDir = Paths.get("logs");
String targetFileName = "itwanger.txt";
FindFileVisitor findFileVisitor = new FindFileVisitor(targetFileName);
try {
Files.walkFileTree(startingDir, findFileVisitor);
if (findFileVisitor.isFileFound()) {
System.out.println("Fajl pronađen: " + findFileVisitor.getFoundFilePath());
} else {
System.out.println("Ooops, fajl nije pronađen");
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static class FindFileVisitor extends SimpleFileVisitor<Path> {
private String targetFileName;
private Path foundFilePath;
public FindFileVisitor(String targetFileName) {
this.targetFileName = targetFileName;
}
public boolean isFileFound() {
return foundFilePath != null;
}
public Path getFoundFilePath() {
return foundFilePath;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString();
if (fileName.equals(targetFileName)) {
foundFilePath = file;
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
}
}U glavnoj metodi koristimo Files.walkFileTree za obilazak stabla fajlova i prosleđujemo početni direktorijum i instancu FindFileVisitor. Nakon obilaska proveravamo da li je ciljni fajl pronađen; ako jeste, štampamo njegovu putanju.
Rezultat pretrage prikazan je u nastavku:

Kratak rezime
Paths i Files su dve ključne klase u Java NIO-u. Paths pruža niz statičkih metoda za rad sa putanjama (Path objektima). Može pretvoriti string ili URI u Path objekat, čime se olakšava dalji rad. Klasa Files pruža bogat skup metoda za rad sa fajlovima — kreiranje, brisanje, pomeranje, kopiranje, čitanje i pisanje. Ove metode podržavaju različite opcije i atribute, poput prepisivanja, zadržavanja atributa i obrade simboličkih linkova. Files takođe podržava obilazak fajlova (npr. metoda walkFileTree) i može obraditi stablo direktorijuma. Ukratko, Paths i Files pružaju jednostavne i efikasne metode za rad sa fajlovima i direktorijumima.
